🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Bump map implementation

Started by
3 comments, last by JoeJ 1 year, 10 months ago

I'm writing a WebGL code in fragment shader for bump maps. I have interpolated position (pos) normal (nrm) and UVs (vuv), so I use derivatives to get positions and height texels on neighbor pixels, then calculate slopes in both x and y directions and then take slope normal. Then I rotate nrm by the angle between the slope normal and unmodified surface normal. Here's the code I have so far:

vec2 nuvx = vuv + dFdx(vuv);
vec2 nuvy = vuv + dFdy(vuv);
vec3 htex = vec3(
	texture2D(bmap, nuvx).g,
	texture2D(bmap, nuvy).g,
	texture2D(bmap, vuv).g
);
vec2 hdif = vec2(htex.x - htex.z, htex.y - htex.z);
if(abs(hdif.x) > 0.002 || abs(hdif.y) > 0.002) {
	vec3 posdx = dFdx(pos);
	vec3 posdy = dFdy(pos);
	vec3 facenorm = normalize(cross(posdx, posdy));
	vec2 hdifsc = bmfact * hdif;
	vec3 slopedx = posdx + facenorm * hdifsc.x;
	vec3 slopedy = posdy + facenorm * hdifsc.y;
	vec3 slope = normalize(cross(slopedx, slopedy));
	vec4 q = quaternFromVects(facenorm, slope); 
	nrm = quaternRotate(nrm, q);
}

vec4 quaternFromVects (vec3 v1, vec3 v2) {
	vec3 qe = normalize(cross(v1, v2));
	float th = acos(dot(v1, v2)) / 2.0;
	float sine = sin(th);
	return vec4(cos(th), qe * sine);
}
vec3 quaternRotate (vec3 v, vec4 q) { // q[0] = w
	vec4 q2 = q * q;
	vec3 q0q123 = q.x * q.yzw;
	vec2 q1q23 = q.y * q.zw;
	float q2q3 = q.z * q.w;

	mat3 mrot;
	mrot[0] = vec3(q2.x + q2.y - q2.z - q2.w, 2.0 * (q0q123.z + q1q23.x), 2.0 * (q1q23.y - q0q123.y));
	mrot[1] = vec3(2.0 * (q1q23.x - q0q123.z), q2.x - q2.y + q2.z - q2.w, 2.0 * (q0q123.x + q2q3));
	mrot[2] = vec3(2.0 * (q0q123.y + q1q23.y), 2.0 * (q2q3 - q0q123.x), q2.x - q2.y - q2.z + q2.w);

	return mrot * v;
}

It looks like it's working but I still wanted to ask:

  1. does this approach make sense?
  2. do I make things more complex than they need to be, maybe these is a way to simplify this code?

thank you!

Advertisement

An alternative would be to convert the bump map to normal map, working in tangent space.

alikim said:
maybe these is a way to simplify this code?

Some optimizations related to quaternions:

To rotate just one vector, it's faster to do so by using quaternion directly, instead making a 3x3 matrix:

vec3 temp = 2 * cross(q.xyz, v);
vec3 rotatedV = v + q.w * temp + cross(q.xyz, temp);

To make a quaternion rotation from one vector to the other, no slow trigonometry is needed:

	__forceinline void FromUnitVecToUnitVec (const sVec3 &dir0, const sVec3 &dir1)
	{
		sScalar dot = dir0.Dot(dir1);
		(*this)[3] = 1.0f + dot;
		if ((*this)[3] >= FP_EPSILON)
		{ 
			*((sVec3*)this) = dir0.Cross(dir1);
			Normalize();
		}
		else
		{
			//if (dot<0) *this = sQuat (1.0f, 0, 0, 0); // 180 degrees apart -> 180 degrees rotation around x
			if (dot<0) *this = sQuat (0, 0, 1.0f, 0); // 180 degrees apart -> 180 degrees rotation around z (using x axis gave problems with uv solver, so switched to z axis)
			else *this = sQuat (0, 0, 0, 1.0f); // zero vector input -> zero rotation output
		}
	}
	__forceinline void FromVecToVecNonUniformLength (const sVec3 &dir0, const sVec3 &dir1)
	{
		sScalar dot = dir0.Dot(dir1);
		(*this)[3] = sqrtf(dir0.SqL() * dir1.SqL()) + dot;
		if ((*this)[3] >= FP_EPSILON)
		{ 
			*((sVec3*)this) = dir0.Cross(dir1);
			Normalize();
		}
		else
		{
			//if (dot<0) *this = sQuat (1.0f, 0, 0, 0); // 180 degrees apart -> 180 degrees rotation around x
			if (dot<0) *this = sQuat (0, 0, 1.0f, 0); // 180 degrees apart -> 180 degrees rotation around z (using x axis gave problems with uv solver, so switched to z axis)
			else *this = sQuat (0, 0, 0, 1.0f); // zero vector input -> zero rotation output
		}
	}

Code is ugly, but still readable i hope. I use xyzw convention.

Thank you, that'll be noticeably faster, especially in GLSL!

vec3 temp = 2 * cross(q.xyz, v);
vec3 rotatedV = v + q.w * temp + cross(q.xyz, temp);

I haven't seen this formula before, curiously enough, the formula in quaternRotate doesn't collapse to this one, they are different but both give the correct result. The mystery of the 4th dimension I guess ?

alikim said:
The mystery of the 4th dimension I guess ?

With a little visualization work, i managed to understand this geometrically :D (but then i forgot about it)

With some more work, i was even able to reinvent / understand quaternion multiplication, but my result caused some more instructions, so i was not happy. (and i forget about that quickly too)

There is some new (to me) approach to understand it: https://marctenbosch.com/quaternions/
Not​


This topic is closed to new replies.

Advertisement