Phat Lewt: Drawing a Diamond

Phat Lewt: Drawing a
Diamond
David Gosselin
3D Application Research Group
ATI Research, Inc.
Overview
• Background
• Refractions
• Reflections
• Sparkles
• Demo
GDC 2004 – Rendering a Diamond
2
What Happens in the Real World
•
Light from the environment
can take multiple paths to
get to the eye
•
High index of refraction
(IR) causes high visual
complexity because light
bounces due to total
internal reflection
GDC 2004 – Rendering a Diamond
3
Basic Algorithm
•
•
Draw back face refractions to the
back buffer
Additively blend on top of back face
refractions:
– Front Face Refractions
– Front Face Reflections
(Environment Cube Map)
– Front Face Specular Lighting
•
Draw sparkles based on Illumination
GDC 2004 – Rendering a Diamond
4
Faking Refractions
•
•
Look up into a refraction cubemap
Use multiple refraction vectors
– Straight up refraction vector
– Refraction with different IR, then reflected by
a vector random to each face
•
•
•
To prevent sampling close to first refraction ray
Use multiple normals (lerp between
smooth and face for more variation)
Can also add an “edge” map to give even
more hard edges (more visual complexity)
GDC 2004 – Rendering a Diamond
5
Creating a Refraction Cubemap
•
Rendered with Maya
•
Camera inside of
gem looking out
•
Lighting environment
approximated by an
environment map
GDC 2004 – Rendering a Diamond
6
Computing Refraction Rays
•
Derived from Snell’s law:
η inside sin(θ inside ) = η outside sin(θ outside )
⎛ η outside
θ inside = sin −1 ⎜⎜
⎝ η inside
⎞
sin(θ outside ) ⎟⎟
⎠
θo
θi
float3 SiTransmissionDirection (float fromIR, float toIR,
float3 incoming, float3 normal)
{
float eta = fromIR/toIR; // relative index of refraction
float c1 = -dot(incoming, normal); // cos(theta1)
float cs2 = 1.-eta*eta*(1.-c1*c1); // cos^2(theta2)
float3 v = (eta*incoming + (eta*c1-sqrt(cs2))*normal);
if (cs2 < 0.) v = 0; // total internal reflection
return v;
}
GDC 2004 – Rendering a Diamond
7
Refractions
Into Back Buffer
(
)*
+
Back Face Refractions
=
Edge
Additive Blend With Back Buffer
(
)*
+
Front Face Refractions
+
=
Edge
GDC 2004 – Rendering a Diamond
8
Combined Refractions
GDC 2004 – Rendering a Diamond
9
Vertex Shader
VsOutput main (VsInput i)
{
// Matrix Skin position
VsOutput o;
float4x4 mSkinning = SiComputeSkinningMatrix (i.weights,
i.indices);
float4 pos = mul (i.pos, mSkinning);
o.pos = mul (pos, mVP);
// Texture coordinates
o.uv = i.uv;
o.noiseUV = dot (i.normal, float3(1, 1, 1));
// Compute normal and perturbed normal
float3 faceNormal = mul (i.normal, mSkinning);
float3 smoothNormal = mul (i.normal2, mSkinning);
float3 mixedNormal = normalize (lerp (faceNormal,
smoothNormal, 0.3));
o.normal = faceNormal;
o.normal2 = mixedNormal;
// Compute Light and view vector
. . .
GDC 2004 – Rendering a Diamond
10
Refraction Pixel Shader
float4 main (PsInput i) : COLOR
{
// Normalize interpolated vectors
float3 vNorm2 = normalize(i.normal2);
float3 vView = normalize(i.view);
// Compute refraction vectors
float3 vRefract = SiTransmissionDirection (1.0, 2.4,
i.view, vNormal2);
float3 vReflectRefract = SiTransmissionDirection (1.0, 1.8,
i.view, vNormal2);
// Reflect second vector by a vector random to each face
float3 rnd = tex2D(tNoise, i.noiseUV);
rnd = normalize (SiConvertColorToVector (rnd));
vReflectRefract = SiReflect (vReflectRefract, rnd);
...
GDC 2004 – Rendering a Diamond
11
Refraction Pixel Shader
...
// Lookup into refraction cubemap and apply gamma
float3 cRefract = texCUBE (tRefraction, vRefract);
cRefract += texCUBE (tRefraction, vReflectRefract.yxz);
cRefract = pow (cRefract, 4.0);
// Edge term
float3 edge = lerp (1.0, tex2D (tEdge, i.uv.xy), 0.4);
// Final Output
float4 o;
o.rgb = cRefract * edge * 0.5; // 0.7 for Front Faces
o.a = 0.0;
return o;
}
GDC 2004 – Rendering a Diamond
12
Reflections
•
•
Reflection cube map lookup
To fake dispersion:
– “Rainbow” cubemap lookup
– Modulate rainbow sample with reflection
sample
•
•
•
Lerp between modulated and original
reflection sample to control dispersion
strength
Modulate with Fresnel term
Add specular highlights
GDC 2004 – Rendering a Diamond
13
Cube Maps
Blurred Environment Map
Rainbow Map
GDC 2004 – Rendering a Diamond
14
Environment Lighting
=
*
Environment Map
LERP
(
,
Rainbow Map
0.5
,
)=
Fresnel Term
*
=
GDC 2004 – Rendering a Diamond
15
Final Look
Refractions
Environment Lighting
Specular Lighting
+
GDC 2004 – Rendering a Diamond
16
Final Front Face Pixel Shader
float4 main (PsInput i) : COLOR
{
// Compute refraction vectors as shown previously
. . .
// Specular Lighting
float3 vReflection = SiReflect (vView, vNormal);
float RdotL = saturate (dot (vReflection, i.lightVec0);
float3 specular = (pow (RdotL, specPower) * lightColor0);
RdotL = saturate (dot (vReflection, i.lightVec1);
specular += (pow (RdotL, specPower) * lightColor1);
RdotL = saturate (dot (vReflection, i.lightVec2);
specular += (pow (RdotL, specPower) * lightColor2);
// Look up environment map
float3 vReflection2 = SiReflect (vView, vNormal2);
float3 cEnv = texCUBE (tEnvironment, vReflection2);
float3 cSpectral = texCUBE (tSpectral, vReflection2);
...
GDC 2004 – Rendering a Diamond
17
Final Front Face Pixel Shader
...
// Combine Environment and Rainbow (spectral) maps
float fresnel = pow(1.0–saturate(dot (vNormal, vView), 2.0);
cEnv = lerp (cEnv, cSpectral * cEnv, 0.5);
cEnv = fresnel * cEnv;
// Put it all together
float4 o;
// Refractions
o.rgb = cRefract * edge * 0.7;
// Environment lighting
o.rgb += (cEnv*reflectionStrength*cReflectionColor);
// Specular lighting
o.rgb += saturate(specular)
o.a = 1.0;
return o;
}
GDC 2004 – Rendering a Diamond
18
Sparkles
•
•
•
Placed at strategic points on geometry
Sparkles move rigidly with gem
Expanded based on their texture coords
– Screen-aligned
•
•
Faded in based on an off-screen texture
luminance at center of sparkle
Modulate with a noise value to make
them flicker a little bit
GDC 2004 – Rendering a Diamond
19
Flare Geometry
•
•
•
Only center matters
“Cloud” works well
No need to reside only on
faces, inside gem works too
Single Flare
Flares
Positioned
GDC 2004 – Rendering a Diamond
20
Conceptual Flare Process
Off-screen buffer
Flare Geometry
For each flare
– Look up luminance of its center in off-screen
– If luminance is > threshold
•
Draw (in reality don’t kill)
GDC 2004 – Rendering a Diamond
21
Sparkle Vertex Shader
VsOutput main (VsInput i)
{
// Skin center of flare
VsOutput o;
float4x4 mSkinning = SiComputeSkinningMatrix (i.weights,
i.indices);
float4 pos = mul (float4(0,0,0,1), mSkinning);
o.pos = mul (pos, mVP);
// Figure out texture coordinates for off-screen texture
o.screenUV = o.pos.xy/o.pos.w;
o.screenUV.y = -o.screenUV.y;
o.screenUV = 0.5 * o.screenUV + 0.5;
// Scale flare in post transform space
float fRadius = 10.0;
o.pos.xy += fRadius * 2 * // Flare size
(i.texCoord - float2(0.5, 0.5)) *
mP._m00_m11; // Scale based on projection matrix
...
GDC 2004 – Rendering a Diamond
22
Sparkle Vertex Shader
...
// Compute View vector
float3 view = normalize (worldCamPos - pos);
// Pass along texture coordinate
o.texCoord = i.texCoord;
// Compute texture coordinates for the noise map
float rnd = dot (pos.xyz, float3(1, 1, 1);
o.noiseUV.x = fmod (abs (rnd)), 2.0f);
rnd = dot (view, float3(1, 1, 1));
o.noiseUV.y = fmod (abs (2.0 * rnd)), 2.0f);
return o;
}
GDC 2004 – Rendering a Diamond
23
Sparkle Pixel Shader
float4 main (PsInput i) : COLOR
{
// Get noise value for flare intensity and size
float noise = tex2D (tNoise, i.noiseUV);
noise = lerp (0.6, 1.0, noise);
// Get off-screen luminance at flare center
float3 cScreen = tex2D (tScreen, i.screenUV);
float lum = dot (cScreen, float3 (0.3, 0.59, 0.11));
clip (lum - 0.8); // Kill pixels that are not bright enough
// Compute the output color based on luminance
lum = smoothstep (0.8, 1.0, lum);
lum *= lum;
// Compute final lighting
float4 o;
o.rgb = noise * lum;
o.a = tex2D (tAlpha, i.texCoord);
return o;
}
GDC 2004 – Rendering a Diamond
24
Demo
GDC 2004 – Rendering a Diamond
25