Jump to content

  • Log In with Google      Sign In   
  • Create Account

liuzewei

Member Since 12 Jan 2009
Offline Last Active Jan 12 2012 07:19 PM

Topics I've Started

[DeferredLighting+iPad/iPhone tested]Compress specular power and specular scale value i...

02 January 2012 - 04:25 PM

Hi,
My english is bad, but I just wanna share something here, because I learned alot in this forum. Maybe these stuff will help someone.

Nowadays, I wanna to implement defered rendering on mobile device(ipad/iphone), because my project needs geometry-independent ligths, so many lights...

After some searching and hard trying, I figured out these devices are not support MRT, and even not support half or single presicion float render target/texture.

So, G-BUFFER is the first and biggest problem. It must use multi-pass technique to generate G-BUFFER.
Have no choice is not a problem, so keep going.
I use three RT and rendering pass to generate G-BUFFER.

[PASS ONE]
POSITION-RT: GL_RGBA GL_UNSIGNED_BYTE
Compressed the position's depth value(float32 in projection space) into this RGBA8 RT, then unpacked/trans back to view-space in lighting stage.
GOOGLE: pack 32-bit float into RGBA(8-bit per channel) render target

[PASS TWO]
NORMAL-RT: GL_RGBA GL_UNSIGNED_BYTE
RGB stored the normal, alpha channel store shadow mask(main directional light's shadow)

[PASS THREE]
COLOR-RT: GL_RGBA GL_UNSIGNED_BYTE
RGB stored the albedo. and alpha channel is free till now.

It's just one 8-bit alpha channel remain, but still have two elements to store.
These are specluar power and specular scale value.
So, Here have two choice:
1. add a new RT and rendering pass, means rendering the entire scene more one time.
2. compress these two value in the free 8-bit space.
Which is the better choice? problem come.

I just cant accept rendering the entire scene four times, so compress is my choice.

[MAIN CONCEPT]
give scale value a [0, 2] range.
give it 3-bit can represent 2^3=8 values in theory.
precision is 2/8=0.25(0, 0.25, 0.5, 0.75 ... 2.0), this is acceptable.

give power value a [0, 200] range.
give it 5-bit can represent 2^5=32 value.
precision is 200/32=6.25(0, 6, 12, 18...200), this "average-kind" of distribution is not acceptable.

[KEY CONCEPT]
I wanna more precision on small power values, and less precision on large ones, when the power value become larger and larger.
someting like this:
0, 0.4, 1.2, 2.4, 4, 6, 8.4, 11.2, 14.4, 18, 22, 26.4 ...... 200
yes, the key-idea for this compression is "arithmetic progression".


----------------------------------------------------------------------
-- below is implement code snippet
----------------------------------------------------------------------
[deferred_gbuffer_normal.fsh]

uniform highp float uSpcPower;
uniform highp float uSpcScale;

varying highp vec3 vNormal;

void main()
{
	// normal
	gl_FragColor.rgb = 0.5 * (normalize(vNormal) + 1.0);
  
	// compress specular power value and specular scale value into 8-bit alpha channel
	//
	// compress
	// d: 0.43 step length
	// s(n) = s_n = (n^2*d - n*d) * 0.5			   range: [0, 199]
	// n(s) = n_s = (d + sqrt(d^2 + 8*d*s_n)) / (2*d) range: [0, 31]
	//
	// power value range: [0, 199] aka s_n, precision: n*d(in other words, precision decreasing when power value become larger and larger)
	// scale value range: [0, 2)   precision: 2/8 = 0.25
	// final value range: [0, 1)
	//
	// output
	//	integer part: floor(n_s+0.5)			range: [0, 31]
	//	float   part: fract(uSpcScale*0.5)	  range: [0, 1)
	//	final  value: (integer_part + float_part) / 32  range: [0, 1)
	//
	// uncompress
	//	alpha channel -> val
	//	power value: (floor(val*32)^2*d - floor(val*32)*d) * 0.5
	//	scale value: fract(val*32) * 2.0
	//
	// for example
	//	 original power value: 0.5 -> integer part: 2
	//	 original scale value: 1.6 -> float   part: 0.8
	//	 final output   value: 0.0875
	//
	//	 uncompressed power value: 0.43
	//	 uncompressed scale value: 1.44
	//
	/* conceptual version
	const highp float d = 0.43;
  
	highp float s_n = uSpcPower;
	highp float n_s = (d + sqrt(d*d + 8.0*d*s_n)) / (2.0*d);
	highp float integer_part = floor(n_s + 0.5);
	highp float float_part = fract(uSpcScale * 0.5);
	highp float final_value = (integer_part + float_part) / 32.0;
  
	gl_FragColor.a = final_value;*/
  
	// optimized version
	highp float n_s = (0.43 + sqrt(0.1849 + 3.44*uSpcPower)) * 1.1627907;
	highp float integer_part = floor(n_s + 0.5);
	highp float float_part = fract(uSpcScale * 0.5);
	gl_FragColor.a = (integer_part + float_part) * 0.03125;
}


[deferred_lighting_point.fsh]

uniform highp mat4 uInvPMat;
uniform highp vec3 uLightPos;
uniform highp vec3 uLightClr;
uniform highp float uLightRadius;
uniform lowp sampler2D uSamplePosition;
uniform lowp sampler2D uSampleNormal;

varying highp vec4 vTexCoord;

const highp vec4 unpackFactors = vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0);

void main()
{
	highp vec2 screenPos = vTexCoord.xy / vTexCoord.w;
	highp vec2 texCoord = (screenPos + 1.0) * 0.5;

	// unpack pos
	highp float depthVal = dot(texture2D(uSamplePosition, texCoord), unpackFactors);
	depthVal = 2.0 * depthVal - 1.0;
	highp vec4 texPos;
	texPos.xy = screenPos;
	texPos.z = depthVal;
	texPos.w = 1.0;
	texPos = uInvPMat * texPos;
	texPos /= texPos.w;

	mediump vec4 texNorm = texture2D(uSampleNormal, texCoord);

	// dif lgt
	highp vec3 lgtDir = uLightPos - texPos.xyz;
	highp float lenLgtDir = length(lgtDir);
	lgtDir /= lenLgtDir;
	highp float attLgt = max(0.0, 1.0 - lenLgtDir/uLightRadius);

	highp vec3 norm = 2.0 * texNorm.rgb - 1.0;
	highp float NdL = max(0.0, dot(norm, lgtDir));
	highp vec3 difLgt = attLgt * NdL * uLightClr;

	// spc lgt

	// uncompress specular power value and specular scale value from 8-bit alpha channel
	//	alpha channel -> val
	//	power value: (floor(val*32)^2*d - floor(val*32)*d) * 0.5
	//	scale value: fract(val*32) * 2.0
	highp float scaleBackValue = texNorm.a * 32.0 + 0.004;
	highp float integer_part = floor(scaleBackValue);
	highp float float_part = fract(scaleBackValue);
  
	highp float spcPower = (integer_part*integer_part*0.43 - integer_part*0.43) * 0.5;
	highp float spcScale = float_part * 2.0;

	highp vec3 rflDir = normalize(reflect(-lgtDir, norm));
	highp vec3 dirToCam = normalize(-texPos.rgb);
	highp float spcLgt = attLgt * spcScale * pow(max(0.0, dot(rflDir, dirToCam)), spcPower);
	gl_FragColor.rgb = difLgt;
	gl_FragColor.a = spcLgt;
}


----------------------------------------------------------------------
-- and a screen shot
-- this is captured on windows but I have tested on ipad, it's ok also.
----------------------------------------------------------------------
Attached File  shot.jpg   127.32KB   69 downloads

zewei, liu

PARTNERS