• Stars
    star
    149
  • Rank 248,619 (Top 5 %)
  • Language
  • Created over 5 years ago
  • Updated over 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

revisiting a known normal transformation in computer graphics

The details of transforming normals

Have you ever seen transpose(inverse(M)) * normal in code before when transforming normals?

This is the defacto solution to dealing with non-uniform scale or skewed models when transforming normals and it's such an accepted practice that nearly every single graphics programming resource mentions and encourages it. The problem is it's wrong.

How did we get here?

A geometric normal is fully defined by its orientation with respect to a surface and the fact that it's orthogonal / perpendicular to the tangent plane at the surface point.

When transforming a normal we want something that preserves both of those constraints. The inverse transpose matrix used to transform the normal is derived from satisfying just the latter. That is, the dot product should equal zero. What we should be using instead is the cross product as it enforces both.

Not easily persuaded? Take this very simple triangle

Now reflect it around the zy axis. You'll note that the normal does not reflect properly if transpose(inverse(M)) is used to transform it. In fact, the normal points in the culled direction. Now all your lighting calculations end up as

max(0, dot(n, l)) = 0

Lets take a look at the derivation with the dot product to see what is actually happening here

We arrive to that by using this identity

From the above we see that we need transpose(inverse(M)) In front of N in order to preserve orthogonality.

What is not so obvious here is that

Has more than one solution and orientation isn't considered. This is easy to show.

When the normal is not zero (|N| != 0) and the triangle isn't degenerate (|V| != 0) you get

Which means when you have M such that

Then you will get a normal that points in the completely opposite direction.

Looking at it a bit differently

So what happens if we derive the normal from the cross product of three vertices instead?

Then we apply M to the vertices to get something more interesting

Where cof here is the cofactor

This tells us something rather interesting

The transpose(inverse(M)) is missing the sign from det(M) and that's why the normal is oriented the wrong way when det(M) < 0.

What we're actually interested in using is the cofactor instead of transpose(inverse(M)), which has the added benefit of being more efficent to compute and more accurate.

Insight to be had

We should not be deriving the normal from the dot product because it leads to precisely this problem. Derive it from the cross product and teach the derivation of it using the cross product.

Sample code

Included here is some sample C code for calculating the cofactor of a 4x4 matrix which can be used instead of transpose(inverse(M))

float minor(const float m[16], int r0, int r1, int r2, int c0, int c1, int c2) {
  return m[4*r0+c0] * (m[4*r1+c1] * m[4*r2+c2] - m[4*r2+c1] * m[4*r1+c2]) -
         m[4*r0+c1] * (m[4*r1+c0] * m[4*r2+c2] - m[4*r2+c0] * m[4*r1+c2]) +
         m[4*r0+c2] * (m[4*r1+c0] * m[4*r2+c1] - m[4*r2+c0] * m[4*r1+c1]);
}

void cofactor(const float src[16], float dst[16]) {
  dst[ 0] =  minor(src, 1, 2, 3, 1, 2, 3);
  dst[ 1] = -minor(src, 1, 2, 3, 0, 2, 3);
  dst[ 2] =  minor(src, 1, 2, 3, 0, 1, 3);
  dst[ 3] = -minor(src, 1, 2, 3, 0, 1, 2);
  dst[ 4] = -minor(src, 0, 2, 3, 1, 2, 3);
  dst[ 5] =  minor(src, 0, 2, 3, 0, 2, 3);
  dst[ 6] = -minor(src, 0, 2, 3, 0, 1, 3);
  dst[ 7] =  minor(src, 0, 2, 3, 0, 1, 2);
  dst[ 8] =  minor(src, 0, 1, 3, 1, 2, 3);
  dst[ 9] = -minor(src, 0, 1, 3, 0, 2, 3);
  dst[10] =  minor(src, 0, 1, 3, 0, 1, 3);
  dst[11] = -minor(src, 0, 1, 3, 0, 1, 2);
  dst[12] = -minor(src, 0, 1, 2, 1, 2, 3);
  dst[13] =  minor(src, 0, 1, 2, 0, 2, 3);
  dst[14] = -minor(src, 0, 1, 2, 0, 1, 3);
  dst[15] =  minor(src, 0, 1, 2, 0, 1, 2);
}

More Repositories

1

incbin

Include binary files in C/C++
C
946
star
2

moreram

Get more system memory
C
804
star
3

breaking_the_physical_limits_of_fonts

Breaking the physical limits of fonts
JavaScript
319
star
4

glsl-parser

A GLSL parser
C++
251
star
5

lambdapp

Anonymous functions in C
C
247
star
6

gmqcc

An Improved Quake C Compiler
C++
159
star
7

codin

Odin to C compiler
C
141
star
8

fpinspect

Inspect floating point computations
C
135
star
9

cvec

No bullshit vector library for C
C
78
star
10

neothyne

Engine and game
C++
77
star
11

libintrusive

Intrusive data structures for C
C
55
star
12

NVFC

OpenSource tool for monitoring, configuring and overclocking NVIDIA GPUs
C
44
star
13

scope_stack_alloc

A scoped stack allocator
C++
36
star
14

deshade

dump and replace shaders of any OpenGL or Vulkan application
C++
29
star
15

gml

Dynamically typed, higher-order, semi-functional, interpreted and embeddable programming language
C
28
star
16

0xABAD1DEA

Static global objects with constructors and destructors made useful in C++
C++
27
star
17

fibers

The fiber sourcebook
HTML
21
star
18

smbf

Static model binary format
C++
19
star
19

gmrtdxt

Realtime DXT compressor and optimizer
C++
19
star
20

fpot

Fast Point Overlap Test
C
17
star
21

dep_sort

Generic topological sorting for sorting a list of dependencies in C++17
C++
13
star
22

wfstd

Standard library I developed while working for Wayforward
C
11
star
23

vector_benchmark

Benchmarking a trivial replacement for std::vector
C++
11
star
24

alice

A barebones kernel for i386
C
10
star
25

bbgl

OpenGL Rendering as a seperate process (Black Box)
C
10
star
26

redroid

The ultimate IRC bot
C
9
star
27

lua-vec

highly efficent, caching, copy-on-write lua vector math library
Lua
7
star
28

printer-display

Use your printer as a display
C
7
star
29

odin_review

A review of the Odin programming language
HTML
6
star
30

pastes

Just a place where I store my pastes
C
6
star
31

discord-rogue

A tiny rogue like for Discord on nodejs
JavaScript
6
star
32

pds2tc

Public domain S2TC implementation
C
5
star
33

aau

Almost Always Unsigned
HTML
5
star
34

Kaizen

a small, embeddable continous integration framework for small projects
C
4
star
35

libpartial

Partially applied functions for C
C
4
star
36

xcpumemperf

Benchmark to determine cross CPU memory performance for UNIX/POSIX systems
C
3
star
37

zbar-lite

Stripped down light weight version of the zbar library
C
3
star
38

aoc

advent of code 2018 solutions https://adventofcode.com/
C++
3
star
39

glsl-compiler

GLSL compiler targeting a simple SSA IR
3
star
40

smm_video_scraper

Scrape level codes from SMM videos
Python
3
star
41

nra

patch games for nes mini and snes mini to use retroarch cores automatically
C
2
star
42

windows_explicide_runtime_imports

Explicit runtime imports for Windows
C
1
star
43

CV

CV, resume of @graphitemaster
1
star