[DeferredLighting+iPad/iPhone tested]Compress specular power and specular scale value into 8-bit alpha channel

Started by
0 comments, last by bzroom 12 years, 3 months ago
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.
----------------------------------------------------------------------
[attachment=6600:shot.jpg]

zewei, liu
Advertisement
Thank you!

This topic is closed to new replies.

Advertisement