Ice Shader in Ventuz 4 Using HLSL Node

Short tutorial how to create realistic ice shader. Dedicated for advanced Ventuz users.

— 13.01.2016

Today, we would like to show you how to make an ice shader in Ventuz using HLSL node.

When you look at a piece of ice, you can see that it is a little bit transparent, and the image seen through the object is quite distorted and bent. This effect is caused by the refraction of light at the interface between air and ice. The light rays change the direction of propagation of a wave, because the IOR (index of refraction) of these two mediums is different. The IOR of air is 1.00029 and IOR of ice is 1.31.

In typical 3D software (such as 3ds Max, Cinema4d, and Blender) we can easily simulate tracing the path of light and get a photorealistic shader of ice, but these software do not work in real-time. So, to make an ice shader that works in real-time, we recommend using a normal map to fake distortion of the image behind the object. All we need are three textures:

diffuse map (color of the object) normal map (to simulate distortion on the surface) refraction map (the image behind the object. You can use RenderTarget node to render everything on the scene that is behind the object and put it into the shader).

Here is the code for how to prepare samplers to get fields for importing these textures into the HLSL shader:

texture colorTex;
sampler colorTexture = sampler_state
Texture = ;
texture normalTex;
sampler normalTexture = sampler_state
Texture = ;
texture refractionText;
sampler refractionTexture = sampler_state
Texture = ;
The structure for VS_Input and VS_OUTPUT should be as shown below:
struct VS_Input{
float4 position : POSITION;
float2 tex : TEXCOORD0;
float3 Normal : NORMAL;
//The PixelInputType structure has a new refractionPosition variable
//for the refraction vertex coordinatesthat will be passed into the pixel shader.
struct VS_OUTPUT
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
float4 refractionPosition : TEXCOORD1;
float3 Normal : TEXCOORD2;
float3 ViewVec : TEXCOORD3;
float3 Light : TEXCOORD4;

The most important part is, of course, in the Pixel Shader Section. We need to modify the texture of the image that is behind the object. I used a refractionIndex to change texture coordinates in both axes.Then, after normalization of the normal map, you can modify the refract texture coordinates using a prepared normal map and refractionScale multiplier if you would like to have more control of the refraction texture scale.

refractTexCoord.x = float(Input.refractionPosition.x/Input.refractionPosition.w/refractionIndex);
refractTexCoord.y = float(-Input.refractionPosition.y/Input.refractionPosition.w/refractionIndex);
normalMap = tex2D(normalTexture, Input.tex);
normal = ( * 2.0f) - 1.0f;
refractTexCoord = refractTexCoord + (normal.xy * refractionScale);
refractionColor = tex2D(refractionTexture, refractTexCoord);
textureColor = tex2D(colorTexture, Input.tex);

To achieve a more realistic shader we used the Fresnel effect, which we had made earlier:

float4 fresnel = Rzero + (1.0f-Rzero) * pow(1.0f-dot(Input.Normal, Input.ViewVec), fresnel_strenght);

(read more on: And the final lines:

return lerp(refractionColor, textureColor, 1-fresnel) + SpecularColor;

Here is the whole code of this script:

Here is a the scene archive: Ice Cube Scene