Post

Custom Shaders

Custom Shaders

Overview

When you display your 3D models with X_ITE, by default it will use the Gouraud shader. This is a versatile shader that can cover a lot of your rendering needs. If this is not enough there is also a Phong shader available, adjustable with the browser option »Shading« per scripting.

However, you will often want to perform special effects or special cases for your materials. To do this you will need to write a custom shader.

Example

Shader Example

Download ZIP Archive

Shaders and Shader Definition

WebGL uses the GLSL language to write shaders that can be run across all browsers. With X_ITE you create your own shader using ComposedShader and ShaderPart nodes and than attach the ComposedShader to the shader field of an Appearance node and that is a child’s play with Sunrize.

XML Encoding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 4.0/EN" "https://www.web3d.org/specifications/x3d-4.0.dtd">
<X3D profile='Interchange' version='4.0' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-4.0.xsd'>
  <head>
    <component name='Shaders' level='1'/>
  </head>
  <Scene>
    <Viewpoint
        position='9.279771 8.706816 16.22163'
        orientation='-0.83432609774564 0.526445494105168 0.163569876068002 0.712985187365762'
        centerOfRotation='4.5 0 4.5'/>
    <TimeSensor DEF='Timer'
        loop='true'/>
    <Transform>
      <Shape>
        <Appearance>
          <ImageTexture
              url='"image.png"'/>
          <ComposedShader DEF='Shader'
              language='GLSL'>
            <field accessType='inputOnly' type='SFTime' name='set_time'/>
            <ShaderPart>
<![CDATA[data:x-shader/x-vertex,#version 300 es
// Vertex Shader
...
uniform float set_time; // value from set_time field
...
]]>
            </ShaderPart>
            <ShaderPart
                type='FRAGMENT'>
<![CDATA[data:x-shader/x-fragment,#version 300 es
// Fragment Shader
...
uniform sampler2D x3d_Texture2D [1]; // image from ImageTexture node
...
]]>
            </ShaderPart>
          </ComposedShader>
        </Appearance>
        <ElevationGrid
            xDimension='10'
            zDimension='10'/>
      </Shape>
    </Transform>
    <ROUTE fromNode='Timer' fromField='elapsedTime' toNode='Shader' toField='set_time'/>
  </Scene>
</X3D>

Classic VRML Encoding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#X3D V4.0 utf8

PROFILE Interchange

COMPONENT Shaders : 1

Viewpoint {
  position 9.279771 8.706816 16.22163
  orientation -0.83432609774564 0.526445494105168 0.163569876068002 0.712985187365762
  centerOfRotation 4.5 0 4.5
}

DEF Timer TimeSensor {
  loop TRUE
}

Transform {
  children Shape {
    appearance Appearance {
      texture ImageTexture {
        url "image.png"
      }
      shaders DEF Shader ComposedShader {
        inputOnly SFTime set_time

        language "GLSL"
        parts [
          ShaderPart {
            url "data:x-shader/x-vertex,#version 300 es
// Vertex Shader
...
uniform float set_time; // value from set_time field
...
"
          }
          ShaderPart {
            type "FRAGMENT"
            url "data:x-shader/x-fragment,#version 300 es
// Fragment Shader
...
uniform sampler2D x3d_Texture2D [1]; // image from ImageTexture node
...
"
          }
        ]
      }
    }
    geometry ElevationGrid {
      xDimension 10
      zDimension 10
    }
  }
}

ROUTE Timer.elapsedTime TO Shader.set_time

Once the X3D is defined we can now write the vertex and the fragment shader source. This is a simple example where a texture is applied to the geometry.

Vertex Shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#version 300 es

precision mediump float;

// Specify build-in uniforms and ins:

uniform mat4 x3d_TextureMatrix [1];
uniform mat4 x3d_ModelViewMatrix;
uniform mat4 x3d_ProjectionMatrix;

in vec4 x3d_TexCoord0;
in vec4 x3d_Vertex;

// Out for fragment shader:

out vec4 texCoord;

// Uniforms from user-defined fields:

uniform float set_time; // value from set_time field

// main:

void
main ()
{
   texCoord = x3d_TextureMatrix [0] * x3d_TexCoord0;

   // Animate vertex along x-axis.
   gl_Position = x3d_ProjectionMatrix * x3d_ModelViewMatrix * (x3d_Vertex + vec4 (set_time % 1.0, 0.0, 0.0, 0.0));
}

Fragment Shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#version 300 es

precision mediump float;

// Specify build-in uniforms and ins:

uniform sampler2D x3d_Texture2D [1];

in vec4 texCoord;

// Specify build-in out:

out vec4 x3d_FragColor;

// main:

void
main ()
{
   // Use color from texture.
   x3d_FragColor = texture (x3d_Texture2D [0], vec2 (texCoord .s, 1.0 - texCoord .t));
}

Lighting and Transparency

Lighting is enabled if a Material node is used and some lights are on.

Normally the browser automatically determines the alpha treatment based on material, colors, and images, but you can force a alpha mode by setting the alphaMode field of a Appearance node.

Data Type Mapping

A ComposedShader node provides the capability to define custom fields like the Script node it does, these fields are then mapped to GLSL uniform variables. They are automatically updated and can be of any access type (initializeOnly, inputOnly, outputOnly or inputOutput).

Node fields

X3D texture typeGLSL variable type
X3DTexture2DNodesampler2D
X3DTexture3DNodesampler3D
X3DEnvironmentTextureNodesamplerCube

X3D field types to GLSL data types

X3D field typeGLSL variable type
SFBoolbool
SFColorvec3
SFColorRGBAvec4
SFDoublefloat
SFFloatfloat
SFImageint [ ](width, height, comp, array)
SFInt32int
SFMatrix3dmat3
SFMatrix3fmat3
SFMatrix4dmat4
SFMatrix4fmat4
SFNodesee node fields table
SFRotationmat33×3 matrix representation
SFStringnot supported
SFTimefloat
SFVec2dvec2
SFVec2fvec2
SFVec3dvec3
SFVec3fvec3
SFVec4dvec4
SFVec4fvec4

MFBoolbool [ ]
MFColorvec3 [ ]
MFColorRGBAvec4 [ ]
MFDoublefloat [ ]
MFFloatfloat [ ]
MFImageint [ ](width, height, comp, array, width ...)
MFInt32int [ ]
MFMatrix3dmat3 [ ]
MFMatrix3fmat3 [ ]
MFMatrix4dmat4 [ ]
MFMatrix4fmat4 [ ]
MFNodesee node fields table
MFRotationmat3 [ ]3×3 matrix representation
MFStringnot supported
MFTimefloat [ ]
MFVec2dvec2 [ ]
MFVec2fvec2 [ ]
MFVec3dvec3 [ ]
MFVec3fvec3 [ ]
MFVec4dvec4 [ ]
MFVec4fvec4 [ ]

Built-in Variables

A ComposedShader defines a number of special variables for the various shader stages. These built-in variables have special properties. They are usually for communicating with certain fixed-functionality. By convention, all predefined variables start with »x3d_«; no user-defined variables may start with this.

TypeNameComment
uniform floatx3d_LogarithmicFarFactor1_2this is a uniform value for logarithmic depth buffer computed as 1.0 / log2 (farPlane + 1.0).

uniform vec4x3d_ClipPlane [x3d_MaxClipPlanes]clip plane array
uniform x3d_FogParametersx3d_Fogsee table »Uniform Struct x3d_FogParameters«

uniform x3d_LightSourceParametersx3d_LightSource [x3d_MaxLights]see table »Uniform Struct x3d_LightSourceParameters«

uniform floatx3d_AlphaCutoffalphaCutoff value from Appearance
uniform x3d_PointPropertiesParametersx3d_PointPropertiessee table »Uniform Struct x3d_PointPropertiesParameters«
uniform x3d_LinePropertiesParametersx3d_LinePropertiessee table »Uniform Struct x3d_LinePropertiesParameters«
uniform x3d_FillPropertiesParametersx3d_FillPropertiessee table »Uniform Struct x3d_FillPropertiesParameters«

uniform x3d_MaterialParametersx3d_Materialsee table »Uniform Struct x3d_MaterialParameters«

uniform sampler2Dx3d_Texture2D [x3d_MaxTextures]texture from Appearance texture field
uniform samplerCubex3d_TextureCube [x3d_MaxTextures]texture from Appearance texture field
uniform x3d_TextureCoordinateGeneratorParametersx3d_TextureCoordinateGenerator [x3d_MaxTextures]see table »Uniform Struct x3d_TextureCoordinateGeneratorParameters«

uniform ivec4x3d_Viewportviewport position and size
uniform mat4x3d_ProjectionMatrixprojection matrix of the camera
uniform mat4x3d_ModelViewMatrixthis is the product of object's transformation matrix and the inverse x3d_CameraSpaceMatrix
uniform mat3x3d_NormalMatrixobject's normal matrix; this is the inverse transpose of the 3×3 submatrix of x3d_ModelViewMatrix
uniform mat4x3d_TextureMatrix [x3d_MaxTextures]object's texture transform matrix defined by nodes derived from X3DTextureTransformNode
uniform mat4x3d_CameraSpaceMatrixtransformation matrix of the camera

in floatx3d_FogDepthfog depth of the vertex overriding Fog.visibilityRange; available if FogCoordinate is attached
in vec4x3d_Colorcolor of the vertex; available if X3DColorNode is attached
in vec4x3d_TexCoord0texture coordinate of the vertex from channel 0
in vec4x3d_TexCoord1texture coordinate of the vertex from channel 1
in vec4x3d_TexCoord2texture coordinate of the vertex from channel 2
in vec4x3d_TexCoord3texture coordinate of the vertex from channel 3
in vec3x3d_Normalnormal of the vertex
in vec4x3d_Vertexvertex coordinate, required

Uniform Struct x3d_FogParameters

TypeNameComment
vec3color 
floatvisibilityRange 
mat3matrixinverse fog space matrix, rotation and scale components

Uniform Struct x3d_LightSourceParameters

TypeNameComment
inttypex3d_DirectionalLight, x3d_PointLight, x3d_SpotLight
vec3color 
floatambientIntensity 
floatintensity 
vec3attenuation 
vec3locationlocation of light in view space coordinates
vec3direction 
floatbeamWidth 
floatcutOffAngle 
floatradius 
mat3matrixinverse light space matrix, rotation and scale components

Uniform Struct x3d_MaterialParameters

TypeName
floatambientIntensity
vec3diffuseColor
vec3specularColor
vec3emissiveColor
floatshininess
floattransparency

Uniform Struct x3d_PointPropertiesParameters

TypeName
floatpointSizeScaleFactor
floatpointSizeMinValue
floatpointSizeMaxValue
vec3pointSizeAttenuation

Uniform Struct x3d_LinePropertiesParameters

TypeName
boolapplied
intlinetype
floatlineStippleScale
sampler2Dtexture

Uniform Struct x3d_FillPropertiesParameters

TypeName
boolfilled
boolhatched
vec3hatchColor
sampler2Dtexture

Uniform Struct x3d_TextureCoordinateGeneratorParameters

TypeName
intmode
floatparameter [6]

Instancing

ParticleSystem

If the shader node is part of a ParticleSystem node the following attributes and uniforms are available.

TypeNameComment
build-ingl_InstanceIdavailable
in vec4x3d_Particlevec4 (int life, float lifetime, float elapsedTime, int texCoordIndex0)
in vec3x3d_ParticleVelocityvelocity vector of particle
in mat4x3d_ParticleMatrixparticle matrix, should be multiplied with x3d_Vertex
uniform sampler2Dx3d_TexCoordRamptexture coordinate ramp

InstancedShape

If the shader node is part of a InstancedShape node the following attributes and uniforms are available.

TypeNameComment
build-ingl_InstanceIdavailable
in mat4x3d_InstanceMatrixinstance matrix, should be multiplied with x3d_Vertex
in mat3x3d_InstanceNormalMatrixinstance normal matrix, should be multiplied with x3d_Normal

Built-in Constants

Some built-in variables are enumerated and have special values and meanings. The following table list all of them and their corresponding values.

ConstantTypeNameValueComment
X_ITEdefined

x3d_ClipPlaneintx3d_MaxClipPlanes6

intx3d_MaxLights8
x3d_LightTypeintx3d_DirectionalLight1
intx3d_PointLight2
intx3d_SpotLight3

intx3d_MaxTextures4

Logarithmic Depth Buffer

I assume pretty much every 3D programmer runs into Z-buffer issues sooner or later. Especially when doing planetary rendering; the distant stuff can be a thousand kilometers away but you still would like to see fine details right in front of the camera. First enable the logarithmic depth buffer:

1
2
3
4
5
6
7
8
9
Script {
  url "ecmascript:

function initialize ()
{
  Browser .setBrowserOption ('LogarithmicDepthBuffer', true);
}
"
}

To address the issue of the depth not being interpolated in perspectively-correct way, add to the fragment shader:

1
2
3
4
5
6
7
8
9
10
11
#version 300 es

uniform float x3d_LogarithmicFarValue1_2;

void
main ()
{
   ...
   //https://outerra.blogspot.com/2013/07/logarithmic-depth-buffer-optimizations.html
   gl_FragDepth = log2 (1.0 + 1.0 / gl_FragCoord .w) * x3d_LogarithmicFarFactor1_2;
}

Note: Logarithmic depth buffer is automatically enabled if a GeoViewpoint node is bound.

See Also

This post is licensed under CC BY 4.0 by the author.