Skip to content

Instantly share code, notes, and snippets.

@inspirnathan
Created February 5, 2023 15:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save inspirnathan/aec5d735194ba556cad69af15d76c831 to your computer and use it in GitHub Desktop.
Save inspirnathan/aec5d735194ba556cad69af15d76c831 to your computer and use it in GitHub Desktop.
Comparison between two approaches of creating a calcNormal function as seen on https://inspirnathan.com/posts/52-shadertoy-tutorial-part-6/
class Vector2 {
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
'+'(value) {
// value can be Vector2 or number
if (value instanceof Vector2) {
return new Vector2(this.x + value.x, this.y + value.y);
}
return new Vector2(this.x + value, this.y + value);
}
'*'(value) {
// value can be Vector2 or number
if (value instanceof Vector2) {
return new Vector2(this.x * value.x, this.y * value.y);
}
return new Vector2(this.x * value, this.y * value);
}
get xxx() {
return new Vector3(this.x, this.x, this.x);
}
get xxy() {
return new Vector3(this.x, this.x, this.y);
}
get xyx() {
return new Vector3(this.x, this.y, this.x);
}
get xyy() {
return new Vector3(this.x, this.y, this.y);
}
get yxx() {
return new Vector3(this.y, this.x, this.x);
}
get yxy() {
return new Vector3(this.y, this.x, this.y);
}
get yyx() {
return new Vector3(this.y, this.y, this.x);
}
get yyy() {
return new Vector3(this.y, this.y, this.y);
}
}
class Vector3 {
constructor(x = 0, y = 0, z = 0) {
this.x = x;
this.y = y;
this.z = z;
}
'+'(value) {
// value can be Vector3 or number
if (value instanceof Vector3) {
return new Vector3(this.x + value.x, this.y + value.y, this.z + value.z);
}
return new Vector3(this.x + value, this.y + value, this.z + value);
}
'*'(value) {
// value can be Vector3 or number
if (value instanceof Vector3) {
return new Vector3(this.x * value.x, this.y * value.y, this.z * value.z);
}
return new Vector3(this.x * value, this.y * value, this.z * value);
}
get xxx() {
return new Vector3(this.x, this.x, this.x);
}
get xxy() {
return new Vector3(this.x, this.x, this.y);
}
get xxz() {
return new Vector3(this.x, this.x, this.z);
}
get xyx() {
return new Vector3(this.x, this.y, this.x);
}
get xyy() {
return new Vector3(this.x, this.y, this.y);
}
get xyz() {
return new Vector3(this.x, this.y, this.z);
}
get xzx() {
return new Vector3(this.x, this.z, this.x);
}
get xzy() {
return new Vector3(this.x, this.z, this.y);
}
get xzz() {
return new Vector3(this.x, this.z, this.z);
}
get yxx() {
return new Vector3(this.y, this.x, this.x);
}
get yxy() {
return new Vector3(this.y, this.x, this.y);
}
get yxz() {
return new Vector3(this.y, this.x, this.z);
}
get yyx() {
return new Vector3(this.y, this.y, this.x);
}
get yyy() {
return new Vector3(this.y, this.y, this.y);
}
get yyz() {
return new Vector3(this.y, this.y, this.z);
}
get yzx() {
return new Vector3(this.y, this.z, this.x);
}
get yzy() {
return new Vector3(this.y, this.z, this.y);
}
get yzz() {
return new Vector3(this.y, this.z, this.z);
}
get zxx() {
return new Vector3(this.z, this.x, this.x);
}
get zxy() {
return new Vector3(this.z, this.x, this.y);
}
get zxz() {
return new Vector3(this.z, this.x, this.z);
}
get zyx() {
return new Vector3(this.z, this.y, this.x);
}
get zyy() {
return new Vector3(this.z, this.y, this.y);
}
get zyz() {
return new Vector3(this.z, this.y, this.z);
}
get zzx() {
return new Vector3(this.z, this.z, this.x);
}
get zzy() {
return new Vector3(this.z, this.z, this.y);
}
get zzz() {
return new Vector3(this.z, this.z, this.z);
}
}
function length(vec) {
if (vec instanceof Vector2) {
return Math.sqrt(vec.x ** 2 + vec.y ** 2);
}
return Math.sqrt(vec.x ** 2 + vec.y ** 2 + vec.z ** 2);
}
function normalize(vec) {
const vecLength = length(vec);
if (vec instanceof Vector2) {
return new Vector2(vec.x / vecLength, vec.y / vecLength);
}
return new Vector3(vec.x / vecLength, vec.y / vecLength, vec.z / vecLength);
}
function sdSphere(p, r) {
// p is the test point on the surface of the sphere
// r is the radius of the sphere
return length(p) - r;
}
function calcNormal1(p) {
const e = 0.0005; // epsilon
const r = 1.0; // radius of sphere
return normalize(
new Vector3(
sdSphere(new Vector3(p.x + e, p.y, p.z), r) -
sdSphere(new Vector3(p.x - e, p.y, p.z), r),
sdSphere(new Vector3(p.x, p.y + e, p.z), r) -
sdSphere(new Vector3(p.x, p.y - e, p.z), r),
sdSphere(new Vector3(p.x, p.y, p.z + e), r) -
sdSphere(new Vector3(p.x, p.y, p.z - e), r)
)
);
}
function calcNormal2(p) {
const e = new Vector2(1.0, -1.0)['*'](0.0005); // epsilon
const r = 1.0; // radius of sphere
const operand1 = e.xyy['*'](sdSphere(p['+'](e.xyy), r));
const operand2 = e.yyx['*'](sdSphere(p['+'](e.yyx), r));
const operand3 = e.yxy['*'](sdSphere(p['+'](e.yxy), r));
const operand4 = e.xxx['*'](sdSphere(p['+'](e.xxx), r));
const sum = operand1['+'](operand2)['+'](operand3)['+'](operand4);
return normalize(sum);
}
const p = new Vector3(1, 2, 3);
console.log('calcNormal1:', calcNormal1(p));
console.log('calcNormal2:', calcNormal2(p));
/* OUTPUT:
calcNormal1: Vector3 {
x: 0.26726124089009934,
y: 0.534522482802048,
z: 0.8017837267599155
}
calcNormal2: Vector3 {
x: 0.26721624351172774,
y: 0.5345183943192493,
z: 0.8018014500721813
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment