Skip to main content
Rendering Realistic  Ice  Objects ShaderX6 5.1 – Anders Nivfors http://cafe.naver.com/shader 임용균  (guardin@naver.com)
Ice Objects?!?
Method Overview Cracks Air bubbles and particles Reflections of light sources and the environment two-sided refraction Bump mapping Bump masking
Adding Cracks Cracks? 얼음이  0 도 보다 높은 환경에 있을 때 내부가 깨지는 현상 Crack 은 얼음 안에 밝게 곡면으로 나타난다 .  Crack  현상이 강함 Crack  현상이 약함
Adding Cracks Approximation 별도의  Crack Mesh 를 만든다 . 범용적 용도의  Crack Model 을 여러 개 만든다 . 모델마다  Crack Mesh 에 임의의 회전 값과 포지션 값을 적용한다 . Crack Mesh 를  bump-mapped specular 만 계산하여 렌더링 Specular 가 일어나지 않는 부분은 완전히 투명 Backface culling 을 끄고 렌더링 FBO(Frame Buffer Object) 를 이용하여 텍스쳐에 렌더링
Adding Cracks Crack objects Clipping Crack mesh 가 모델 안에 완전히  fit 되지 않는 문제가 있다 . Screen space CSG(Constructive Solid Geometry) 를 응용 Boolean Difference Stencil Buffer 를 두 번째  depth buffer 로 사용 Our Method Ice object 의  front-facing surface 의  depth 값과  back-facing surface 의  depth 값을 텍스쳐에 기록한다 . 최적 해상도의  depth buffer  구하기 v –  카메라에서  ice object 의 중심으로의 벡터의 길이 d – ice object  바운딩 박스의 대각의 길이 Near plane = v – d / 2.0; Far plane = v + d / 2.0;
Adding Cracks Crack objects Clipping Our Method (Continued…) Crack object 를 렌더링 하면서  depth 값이 위의  depth buffer 들을 샘플링 한 사이값이 아니면  pixel 을 렌더링 하지 않는다 .
Air in the Ice Air bubble 얼음이 흰색인 이유는 얼음 안에 공기가 들어 있기 때문 얼음의 중심으로 갈 수록 밀도가 늘어난다 . Approximation 볼륨감 있는  air core 를 만들려면 아래와 같은  two-sided, semi-transparent  텍스쳐를  10~15 번 정도 텍스쳐에 렌더링 한다 . Ice object 의 중심에서 임의의 회전값을 적용 한다 .
Reflection and Two-sided Refraction Reflection Rl = 2.0*(V∙N)N - V Cubic environment maps (cube maps) Two-sided Refraction Rr = 0.5*(V-N)N - V Light refract 는  entry point 와  exit point 의 두  point 가 있으므로  Reflection 보다는 복잡하다 . 가장 쉽고 저렴한  Refraction 은  front-facing surface 에만 적용하는 것인데 품질이 좋지 않다 . One point  Refraction Two point  Refraction
Reflection and Two-sided Refraction Two-sided Refraction (Continued…) Ice object 의  back-side normal  정보가 필요 shader 를 이용해  back-facing face 들의  normal 을 텍스쳐에 렌더링 퍼포먼스 오버헤드를 줄이기 위해  FBO 를 사용한다 . 한 픽셀의  front/back-side normal  정보가 있으므로 두 정보를 이용해 새로운  normal  값을 구한다 . (weight = 0.33) half3 newNormal = normal * (1.0 – weight) – backSideNormal * weight; 물리적으로 맞는 연산은 아니지만 퍼포먼스와의  Tradeoff Ice 는 크게 왜곡되는 재질이므로 이상하지 않다 . Mix the reflection and refraction approximation Fresnel team  f = (1.0 - |V∙N|)^3 V = Eye vector, N = Normal vector Mixed = RefractionColor*f + ReflectionColor*(1.0-f)
Environment Bump Mapping and Masking Bump Mapping Ice 는 크게 왜곡되는 재질이므로  bump map 을 이용하여  detail 을 향상시키고 일관되게 보이지 않게 한다 . Bump Masking Normal map 을 적용할지를 결정하는  grayscale texture 표면을 일관적이지 않게 나타내고  small crack 을 나타낸다 . Bump Map RGB Mask Map Grayscale
Conclusion
Shader Code – IceCrackDepth FragOut mainF(VertOut IN, uniform sampler2D bumpMap, uniform sampler2D depthTexFF, uniform sampler2D depthTexBF,  float4 winpos : WPOS, uniform half bumpStrength) { FragOut OUT; half2 texCoordsProj = 0.5 * (IN.vertPos.xy/IN.vertPos.z) + 0.5; //calculate screen space texture coordinates half4 bumpCol = tex2D(bumpMap, IN.tCoords.xy); //get bumpmap value //normalize vectors, and displace normal with bumpmap IN.normal = normalize(IN.normal - bumpCol.xyz*bumpStrength); IN.viewVec = normalize(IN.viewVec); IN.lightVec = normalize(IN.lightVec.xyz); //calculate reflection and refraction vectors half3 reflectVec = reflect(IN.viewVec, IN.normal); //calculate specular highlight half spec = pow(clamp(dot(IN.lightVec.xyz, -reflectVec), 0.0, 1.0), 20.0); half3 specCol = spec; //DEPTH TEST (don't draw pixels outside the ice model) half4 depthFFCol = tex2D(depthTexFF, texCoordsProj); half4 depthBFCol = tex2D(depthTexBF, texCoordsProj); //if outside the model, draw nothing (alpha=0) half alpha = 1.0; if(winpos.z<depthFFCol.b || winpos.z>depthBFCol.b) alpha = 0.0; OUT.col.xyz = specCol + bumpCol.xxx*0.05; OUT.col.a = alpha; return OUT; }
Shader Code – IceFinal FragOut mainF(VertOut IN,  uniform sampler2D backSide, uniform sampler2D bumpMask, uniform sampler2D bumpMap, uniform samplerCUBE envMap, uniform sampler2D cracks, uniform sampler2D core, uniform half eta,  //relative refraction index uniform half bumpStrength, //how much impact the bumpmap have on the normals uniform half backSideRefr) //how much the backside's normals will affect the refraction { FragOut OUT; //calculate screen space texture coordinates half2 texCoordsProj = 0.5 * (IN.vertPos.xy/IN.vertPos.z) + 0.5; //get bumpmap value half3 bumpCol = tex2D(bumpMap, IN.tCoords.xy); //get bumpmap mask value half3 bumpMaskCol = tex2D(bumpMask, IN.tCoords.xy); //get backside normals half3 backsideNormal = 2*tex2D(backSide, texCoordsProj) - 1; //normalize vectors, and displace normal with bumpmap backsideNormal = normalize(backsideNormal); IN.normal = normalize(IN.normal - bumpCol*bumpStrength*bumpMaskCol); IN.viewVec = normalize(IN.viewVec); IN.lightVec = normalize(IN.lightVec); //calculate reflection and refraction vectors half3 reflectVec = reflect(IN.viewVec, IN.normal); half3 refractVec = refract(IN.viewVec, IN.normal*(1.0-backSideRefr)-backsideNormal*backSideRefr, eta);
Shader Code – IceFinal (Continued…) //calculate specular highlight half spec = pow(clamp(dot(IN.lightVec.xyz, -reflectVec), 0.0, 1.0), 20.0); half3 specCol = spec; //fetch cubemap-texture color values half3 refrCol = texCUBE(envMap, refractVec); half3 reflCol = texCUBE(envMap, reflectVec); //fetch cracks texture color value, distort the texcoords with the worldspace normal of the surface half3 crackCol = tex2D(cracks, texCoordsProj - 0.013*IN.normal.xy); //fetch air core texture color value, distort the texcoords with the worldspace normal of the surface half3 coreCol = tex2D(core, texCoordsProj - 0.02*IN.normal.xy); half3 texColMask = half3(0.15, 0.15, 0.2); //  {[what you see through the ice * mix val.] + [reflection*mix val.] + [texture color from the bumpmap]}*0.9 + specular OUT.col.rgb = (((crackCol+refrCol)*(1.0-coreCol.x)+coreCol)*(1.0-IN.fresnelVal) + reflCol*(IN.fresnelVal) +  bumpCol.rrr*texColMask)*0.88 + specCol; return OUT; }