(self)
| 140 | # Compute tangent space from texture map coordinates. |
| 141 | # Follows http://www.mikktspace.com/ conventions. |
| 142 | def compute_tangents(self): |
| 143 | idx = [ |
| 144 | self.tri_idx[:, 0].type(torch.int64), |
| 145 | self.tri_idx[:, 1].type(torch.int64), |
| 146 | self.tri_idx[:, 2].type(torch.int64), |
| 147 | ] |
| 148 | pos = [self.v_pos[idx[0], :], self.v_pos[idx[1], :], self.v_pos[idx[2], :]] |
| 149 | texcrd = [ |
| 150 | self.v_texcrd[idx[0], :], |
| 151 | self.v_texcrd[idx[1], :], |
| 152 | self.v_texcrd[idx[2], :], |
| 153 | ] |
| 154 | |
| 155 | v_tangents = torch.zeros_like(self.v_norm) |
| 156 | |
| 157 | # Compute tangent space for each triangle. |
| 158 | uve1 = texcrd[1] - texcrd[0] |
| 159 | uve2 = texcrd[2] - texcrd[0] |
| 160 | pe1 = pos[1] - pos[0] |
| 161 | pe2 = pos[2] - pos[0] |
| 162 | |
| 163 | nom = pe1 * uve2[:, 1:2] - pe2 * uve1[:, 1:2] |
| 164 | denom = uve1[:, 0:1] * uve2[:, 1:2] - uve1[:, 1:2] * uve2[:, 0:1] |
| 165 | |
| 166 | # Avoid division by zerofor degenerated texture coordinates. |
| 167 | tang = nom / torch.where( |
| 168 | denom > 0.0, torch.clamp(denom, min=EPS), torch.clamp(denom, max=-EPS) |
| 169 | ) |
| 170 | |
| 171 | # Update all 3 vertices. |
| 172 | for i in range(3): |
| 173 | t_idx = idx[i][:, None].repeat(1, 3) |
| 174 | v_tangents.scatter_add_(0, t_idx, tang) |
| 175 | |
| 176 | # Normalize, replace zero (degenerated) tangents with some default value. |
| 177 | default_tangents = torch.where( |
| 178 | dot( |
| 179 | self.v_norm, |
| 180 | torch.tensor([1.0, 0.0, 0.0], dtype=torch.float32, device="cuda"), |
| 181 | ) |
| 182 | > 0.9999, |
| 183 | torch.tensor([0.0, 1.0, 0.0], dtype=torch.float32, device="cuda"), |
| 184 | torch.tensor([1.0, 0.0, 0.0], dtype=torch.float32, device="cuda"), |
| 185 | ) |
| 186 | v_tangents = torch.where(length(v_tangents) > EPS, v_tangents, default_tangents) |
| 187 | v_tangents = normalize_safe(v_tangents) |
| 188 | |
| 189 | # Make sure tangent is orthogonal to normal. |
| 190 | v_tangents = normalize_safe( |
| 191 | v_tangents - self.v_norm * dot(self.v_norm, v_tangents) |
| 192 | ) |
| 193 | |
| 194 | if torch.is_anomaly_enabled(): |
| 195 | assert torch.all(torch.isfinite(v_tangents)) |
| 196 | |
| 197 | self.v_tangent = v_tangents |
| 198 | return v_tangents |
no test coverage detected