Difference between revisions of "W4 Material Creation"

From Ciliz|W4
 
(18 intermediate revisions by the same user not shown)
Line 7: Line 7:
 
As an example, the material is created based on the [https://www.shadertoy.com/view/4djXzz shader] from the www.shadertoy.com.  
 
As an example, the material is created based on the [https://www.shadertoy.com/view/4djXzz shader] from the www.shadertoy.com.  
  
Use the empty project and the code from [[Common_Knowledge]] page as a basis:
+
Take the following code as the basis:
 
<syntaxhighlight lang="c++">
 
<syntaxhighlight lang="c++">
 
#include "W4Framework.h"
 
#include "W4Framework.h"
Line 62: Line 62:
 
Add the following to '''topologica.vs''':
 
Add the following to '''topologica.vs''':
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
precision highp float;
+
attribute vec3 w4_a_normal;
 
 
attribute vec3 position;
 
attribute vec3 normal;
 
 
 
uniform mat4 projectionView;
 
uniform mat4 model;
 
uniform mat3 normalSpace;
 
uniform vec3 eyePosition;
 
  
 
varying vec3 v_toView;
 
varying vec3 v_toView;
Line 76: Line 68:
 
varying vec3 v_toLight;
 
varying vec3 v_toLight;
  
void main()
+
void w4_main()
 
{
 
{
 
     const vec3 lightPosition = vec3(0., 10., -10.);
 
     const vec3 lightPosition = vec3(0., 10., -10.);
     gl_Position = projectionView * model * vec4(position , 1.0);
+
     gl_Position = w4_u_projectionView * w4_getVertexPosition();
     v_normal  = normalSpace * normal;
+
     v_normal  = w4_u_normalSpace * w4_a_normal;
     v_toView  = eyePosition - gl_Position.xyz;
+
     v_toView  = w4_u_eyePosition - gl_Position.xyz;
 
     v_toLight = lightPosition - gl_Position.xyz;
 
     v_toLight = lightPosition - gl_Position.xyz;
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
All parameters passed to the shader are listed in the [[Built-in Shader Param GLSL]] article. Let’s analyze the values passed to the shader:
+
All parameters passed to the shader are listed in the [[W4_Material_System]] article. All uniforms described above are accessible by default in a shader. The only attribute that was originally added is '''vec3 w4_a_position'''. This attribute is needed to calculate the position in '''w4_getVertexPosition'''
 +
 
 +
Let’s analyze the values passed to the shader:
  
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
| position || attribute|| Vertex position
+
| w4_a_normal || attribute|| Vertex normal. Here it is indicated that in addition to the vertex position, the model also contains a normal.
|-
 
| normal || attribute|| Vertex normal
 
 
|-
 
|-
 
| projectionView || uniform || This parameter is the result of the multiplication of projection and view matrixes.
 
| projectionView || uniform || This parameter is the result of the multiplication of projection and view matrixes.
  
Note. The projection matrix is calculated from camera parameters such as Fov, Aspect, Near, Far.
+
Note. The projection matrix is calculated from camera parameters such as Fov, Aspect, Near, Far. ‘View matrix’ is calculated from the rotation and position of the camera. Since both matrixes are rarely used separately, they reach the shader pre-multiplied
View matrix is calculated from the rotation and position of the camera. Since both matrixes are rarely used separately, they reach the shader pre-multiplied
 
 
|-
 
|-
| model || uniform || The model matrix is the matrix that translates, scales and rotates your object
+
| w4_u_normalSpace || uniform || Normal matrix is the matrix which is used to transform the normal to the view space (camera space)
 
|-
 
|-
| normalSpace || uniform || Normal matrix is the matrix which is used to transform the normal to the view space (camera space)
+
| w4_u_eyePosition || uniform || Camera position
|-
 
| eyePosition || uniform || Camera position
 
 
|}
 
|}
  
 
At this point, the vertex shader defines a fixed point of lighting, calculates the position of a point, and passes the normal to the fragment shader (vector from the current point to the camera and vector from the current point to the light source).
 
At this point, the vertex shader defines a fixed point of lighting, calculates the position of a point, and passes the normal to the fragment shader (vector from the current point to the camera and vector from the current point to the light source).
 +
 +
The vertex position is obtained using the standard '''w4_getVertexPosition''' function. Its implementation depends on the type of surface (static / skeletal animation).
 +
 +
Note that it is the '''w4_main''' function that is described. Do not confuse it with the standard '''main''', which is already included in the basic material template.
 +
 
=== Fragment shader ===
 
=== Fragment shader ===
Create a file called '''topologica.fs''' with the following lines:
+
Create a file named '''topologica.fs''' with the following lines:
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
precision highp float;
+
uniform vec4 baseColor; //user uniform
 
+
uniform vec4 specColor; //user uniform
uniform vec4 baseColor;
 
uniform vec4 specColor;
 
  
 
varying vec3 v_normal;
 
varying vec3 v_normal;
Line 118: Line 110:
 
varying vec3 v_toLight;
 
varying vec3 v_toLight;
  
void main()
+
void w4_main()
 
{
 
{
 
     const float specPower = 10.0;
 
     const float specPower = 10.0;
Line 131: Line 123:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
This shader paints the surface in a defined color, with a single light source of a defined color. Hereinafter, the '''baseColor''' and '''specColor''' parameters are set from code.  
+
This shader paints the surface in a specified color, taking into account that there is only one light source of a defined color. Hereinafter, the '''baseColor''' and '''specColor''' parameters are set from the code.
 +
 
 
=== Add material ===
 
=== Add material ===
 
Create a file with a description of the material in accordance with the [[Material File Format]] article. In our case, topologica.mat contains only the paths to the shader files:
 
Create a file with a description of the material in accordance with the [[Material File Format]] article. In our case, topologica.mat contains only the paths to the shader files:
Line 179: Line 172:
  
 
== Adding a complex shader ==
 
== Adding a complex shader ==
After you made sure that everything works smoothly, you can switch to the [https://www.shadertoy.com/view/4djXzz shader] from the www.shadertoy.com. Its contents are below:
+
After you made sure that everything works smoothly, you can switch to the [https://www.shadertoy.com/view/4djXzz shader] from www.shadertoy.com. Its contents are below:
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
 
/*--------------------------------------------------------------------------------------
 
/*--------------------------------------------------------------------------------------
Line 322: Line 315:
 
Copy the shader from the site to the fragment shader of the project, adjusting some names of parameters. All functions are copied, except for the following:
 
Copy the shader from the site to the fragment shader of the project, adjusting some names of parameters. All functions are copied, except for the following:
  
* '''mainImage''' as it will be copied on the later stage;
+
* '''mainImage''' as it will be copied on later stage;
* '''noise2dT''' as it not used and leads to an error.
+
* '''noise2dT''' as it will is not used and leads to an error.
  
As a result, '''topologica.fs''' looks like this:
+
As a result, '''topologica.fs''' looks the following way:
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
precision highp float;
 
 
 
uniform vec4 baseColor;
 
uniform vec4 baseColor;
 
uniform vec4 specColor;
 
uniform vec4 specColor;
Line 408: Line 399:
 
}
 
}
  
void main()
+
void w4_main()
 
{
 
{
 
     const float specPower = 10.0;
 
     const float specPower = 10.0;
Line 421: Line 412:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
The program with the above shader runs without errors, but the cube has not changed, because the '''mainImage''' part is missing.
+
The program with the above shader runs without errors, but the cube has not changed, as the '''mainImage''' part is missing.
  
Copy the contents of '''mainImage''' to the '''main''' of fragment shader and get the following code:
+
Copy the contents of '''mainImage''' to the '''main''' of fragment shader and you will get the following code:
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
precision highp float;
 
 
 
uniform vec4 baseColor;
 
uniform vec4 baseColor;
 
uniform vec4 specColor;
 
uniform vec4 specColor;
Line 507: Line 496:
 
}
 
}
  
void main()
+
void w4_main()
 
{
 
{
 
     // ---------------- First, set up the camera rays for ray marching ----------------
 
     // ---------------- First, set up the camera rays for ray marching ----------------
Line 568: Line 557:
 
</syntaxhighlight>
 
</syntaxhighlight>
 
==== Debugging ====
 
==== Debugging ====
If you run the program now, errors will appear with the missing '''fragCoord''', '''iResolution''', '''iTime''' and '''iMouse''' variables. Let's consider them in order:
+
If you run the program now, errors will appear with the missing variables: '''fragCoord''', '''iResolution''', '''iTime''' and '''iMouse''' variables. Let's review them case by case:
  
1) '''fragCoord''' and '''iResolution''' are used to calculate '''uv'''. '''uv''' can be taken from the vertex shader ('''v_uv'''). Do not forget to write the following in the headers of both shaders:  
+
1) '''fragCoord''' and '''iResolution''' are used to calculate '''uv'''. '''uv''' can be retrieved from the vertex shader ('''v_uv'''). Do not forget to complete the headers of both shaders with lines accordingly:  
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
 
  varying vec2 v_uv;
 
  varying vec2 v_uv;
Line 576: Line 565:
 
and add the following to the vertex shader:  
 
and add the following to the vertex shader:  
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
attribute vec2 uv0;
+
attribute vec2 w4_a_uv0;
...
+
...
v_uv = uv0;
+
v_uv = w4_a_uv0;
 
</syntaxhighlight>
 
</syntaxhighlight>
Also '''iTime''' - this is the '''time''' attribute. Add it to the fragment shader:
+
'''iTime''' is the '''time''' attribute. Add it to the fragment shader:
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
uniform float time;
+
uniform float w4_u_time;
 
</syntaxhighlight>
 
</syntaxhighlight>
and replace '''iTime''' with '''time'''.
+
replace '''iTime''' with '''w4_u_time'''.
  
 
Hardcode '''iMouse''' (for later use).
 
Hardcode '''iMouse''' (for later use).
Line 590: Line 579:
 
vec2 iMouse = vec2(0, 0);
 
vec2 iMouse = vec2(0, 0);
 
</syntaxhighlight>
 
</syntaxhighlight>
Also '''iResolution''' is used to calculate the aspect ratio. To optimize the execution, let's calculate it in the vertex shader.
+
'''iResolution''' is also used to calculate the aspect ratio. To optimize the execution, let's calculate it in the vertex shader.
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
uniform vec2 resolution;
+
uniform vec2 w4_u_resolution;
 
...
 
...
 
varying float v_aspect;
 
varying float v_aspect;
 
...
 
...
v_aspect = resolution.x / resolution.y;
+
v_aspect = w4_u_resolution.x / w4_u_resolution.y;
 
</syntaxhighlight>
 
</syntaxhighlight>
Note. Don't forget to remove '''baseColor''' from the shader and source code. Since it is no longer used.
+
Note. Since '''baseColor''' is no longer used, don't forget to remove it from the shader and source code.
 +
 
 
== Result ==
 
== Result ==
 
The resulting shaders:
 
The resulting shaders:
Line 604: Line 594:
 
'''topologica.vs'''
 
'''topologica.vs'''
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
precision highp float;
 
 
 
attribute vec3 position;
 
attribute vec3 position;
 
attribute vec3 normal;
 
attribute vec3 normal;
Line 637: Line 625:
 
'''topologica.fs'''
 
'''topologica.fs'''
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
precision highp float;
 
 
 
uniform vec4 specColor;
 
uniform vec4 specColor;
uniform float time;
+
uniform float w4_u_time;
  
 
varying vec3 v_normal;
 
varying vec3 v_normal;
Line 721: Line 707:
 
}
 
}
  
void main()
+
void w4_main()
 
{
 
{
 
     // ---------------- First, set up the camera rays for ray marching ----------------
 
     // ---------------- First, set up the camera rays for ray marching ----------------
Line 733: Line 719:
 
         vec2 iResolution = vec2(1, 1);
 
         vec2 iResolution = vec2(1, 1);
  
     float mx=iMouse.x/iResolution.x*PI*2.0 + time * 0.01;
+
     float mx=iMouse.x/iResolution.x*PI*2.0 + w4_u_time * 0.01;
     float my=-iMouse.y/iResolution.y*10.0 + sin(time * 0.03)*0.2+0.2;//*PI/2.01;
+
     float my=-iMouse.y/iResolution.y*10.0 + sin(w4_u_time * 0.03)*0.2+0.2;//*PI/2.01;
 
     vec3 camPos=vec3(cos(my)*cos(mx),sin(my),cos(my)*sin(mx))*(200.2); // prp
 
     vec3 camPos=vec3(cos(my)*cos(mx),sin(my),cos(my)*sin(mx))*(200.2); // prp
  
Line 782: Line 768:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
'''sample.cpp''' has not changed, except for the deleted line with '''baseColor'''.
+
'''sample.cpp''' has remained unchanged, except for the deleted line with '''baseColor'''.
  
 
Let's run the program:
 
Let's run the program:
Line 788: Line 774:
 
http://demo.w4-dev.ciliz.com/wiki-files/Material_03.png
 
http://demo.w4-dev.ciliz.com/wiki-files/Material_03.png
  
The material added!
+
As a result of the previous steps the material is added.
 +
 
 
== Cursor handler ==
 
== Cursor handler ==
The mouse position can simply be removed from the shader, but let's implement it. To do this, add a mouse movement handler and pass the normalized cursor position to the shader.
+
The mouse position can simply be removed from the shader, but in our case we keep it. This can be done by adding a mouse movement handler and passing the normalized cursor position to the shader.
  
 
The resulting code:
 
The resulting code:
Line 833: Line 820:
 
'''topologica.fs'''
 
'''topologica.fs'''
 
<syntaxhighlight lang="GLSL">
 
<syntaxhighlight lang="GLSL">
precision highp float;
 
 
 
uniform vec4 specColor;
 
uniform vec4 specColor;
uniform float time;
 
 
uniform vec2 mousePos;
 
uniform vec2 mousePos;
  
Line 918: Line 902:
 
}
 
}
  
void main()
+
void w4_main()
 
{
 
{
 
     // ---------------- First, set up the camera rays for ray marching ----------------
 
     // ---------------- First, set up the camera rays for ray marching ----------------
Line 927: Line 911:
 
     vec3 camLookat=vec3(0,0.0,0); // vrp
 
     vec3 camLookat=vec3(0,0.0,0); // vrp
  
     float mx=mousePos.x*PI*2.0 + time * 0.01;
+
     float mx=mousePos.x*PI*2.0 + w4_u_time * 0.01;
     float my=-mousePos.y*10.0 + sin(time * 0.03)*0.2+0.2;//*PI/2.01;
+
     float my=-mousePos.y*10.0 + sin(w4_u_time * 0.03)*0.2+0.2;//*PI/2.01;
 
     vec3 camPos=vec3(cos(my)*cos(mx),sin(my),cos(my)*sin(mx))*(200.2); // prp
 
     vec3 camPos=vec3(cos(my)*cos(mx),sin(my),cos(my)*sin(mx))*(200.2); // prp
  
Line 945: Line 929:
 
     vec3 pos = vec3(0,0,0);
 
     vec3 pos = vec3(0,0,0);
 
         float density = 0.0;
 
         float density = 0.0;
     // ray marching time
+
     // ray marching w4_u_time
 
         for (int i = 0; i < 37; i++) // This is the count of how many times the ray actually marches.
 
         for (int i = 0; i < 37; i++) // This is the count of how many times the ray actually marches.
 
         {
 
         {
Line 984: Line 968:
 
http://demo.w4-dev.ciliz.com/wiki-files/Material_final.mov
 
http://demo.w4-dev.ciliz.com/wiki-files/Material_final.mov
  
When you move the mouse with the button held down, the shader reacts similarly to the shadertoy implementation. Therefore, the functionality is fully transferred!
+
When you move the mouse with the pressed button, the shader reacts similarly to the shadertoy implementation. Therefore, the functionality is fully transferred!

Latest revision as of 15:19, 17 July 2020

Scope

Assuming that you have read the article W4 Physics for Dummies, and are willing to explore the W4 Engine further study the cases below.

This page describes how to create material and use code from external resources.

Basic Information

As an example, the material is created based on the shader from the www.shadertoy.com.

Take the following code as the basis:

#include "W4Framework.h"

W4_USE_UNSTRICT_INTERFACE

struct ShadertoyDemo : public IGame
{
public:
    void onStart() override
    {
        m_cube = Mesh::create::cube({2.f, 2.f, 2.f});
        Render::getRoot()->addChild(m_cube);
    }

    virtual void onTouch(const event::Touch::Begin&) override
    {
    }

    void onUpdate(float dt) override
    {
        m_cube->rotate(Rotator{dt,dt,dt});
    }

private:
    sptr<Mesh> m_cube;
};

W4_RUN(ShadertoyDemo)

Run the program. A spinning cube will appear, as in the figure below:

Material_01.png

The material will be added on a later stage.

Project Structure

The structure of the project:

.
├── resources
   └── materials
       ├── shaders
          ├── topologica.fs
          └── topologica.vs
       └── topologica.mat
└── sources
    └── sample.cpp

In this case, topologia.fs and topologia.vs are the fragment and vertex shaders, accordingly, while topologia.mat is the material file, and sample.cpp is the program code.

Shader Creation

To test the program, let's start with simple vertex and fragment shaders.

Vertex Shader

Add the following to topologica.vs:

attribute vec3 w4_a_normal;

varying vec3 v_toView;
varying vec3 v_normal;
varying vec3 v_toLight;

void w4_main()
{
    const vec3 lightPosition = vec3(0., 10., -10.);
    gl_Position = w4_u_projectionView * w4_getVertexPosition();
    v_normal  = w4_u_normalSpace * w4_a_normal;
    v_toView  = w4_u_eyePosition - gl_Position.xyz;
    v_toLight = lightPosition - gl_Position.xyz;
}

All parameters passed to the shader are listed in the W4_Material_System article. All uniforms described above are accessible by default in a shader. The only attribute that was originally added is vec3 w4_a_position. This attribute is needed to calculate the position in w4_getVertexPosition

Let’s analyze the values passed to the shader:

w4_a_normal attribute Vertex normal. Here it is indicated that in addition to the vertex position, the model also contains a normal.
projectionView uniform This parameter is the result of the multiplication of projection and view matrixes.

Note. The projection matrix is calculated from camera parameters such as Fov, Aspect, Near, Far. ‘View matrix’ is calculated from the rotation and position of the camera. Since both matrixes are rarely used separately, they reach the shader pre-multiplied

w4_u_normalSpace uniform Normal matrix is the matrix which is used to transform the normal to the view space (camera space)
w4_u_eyePosition uniform Camera position

At this point, the vertex shader defines a fixed point of lighting, calculates the position of a point, and passes the normal to the fragment shader (vector from the current point to the camera and vector from the current point to the light source).

The vertex position is obtained using the standard w4_getVertexPosition function. Its implementation depends on the type of surface (static / skeletal animation).

Note that it is the w4_main function that is described. Do not confuse it with the standard main, which is already included in the basic material template.

Fragment shader

Create a file named topologica.fs with the following lines:

uniform vec4 baseColor; //user uniform
uniform vec4 specColor; //user uniform

varying vec3 v_normal;
varying vec3 v_toView;
varying vec3 v_toLight;

void w4_main()
{
    const float specPower = 10.0;
    vec3 n2 = normalize ( v_normal );
    vec3 v2 = normalize ( v_toView );
    vec3 r  = reflect   ( -v2, n2 );
    vec3 l2 = normalize ( v_toLight );
    vec4 diff = baseColor * max ( dot ( n2, l2 ), 0.0 );
    vec4 spec = specColor * pow ( max ( dot ( l2, r ), 0.0 ), specPower );
    gl_FragColor = vec4(diff.rgb, 1.);
    gl_FragColor.rgb += spec.rgb;
}

This shader paints the surface in a specified color, taking into account that there is only one light source of a defined color. Hereinafter, the baseColor and specColor parameters are set from the code.

Add material

Create a file with a description of the material in accordance with the Material File Format article. In our case, topologica.mat contains only the paths to the shader files:

{
    "vertexFile" : "resources/materials/shaders/topologica.vs",
    "fragmentFile" : "resources/materials/shaders/topologica.fs",
}

Program Code (sample.cpp):

#include "W4Framework.h"

W4_USE_UNSTRICT_INTERFACE

struct ShadertoyDemo : public IGame
{
public:
    void onStart() override
    {
        m_cube = Mesh::create::cube({2.f, 2.f, 2.f});
        auto matInst = Material::get("resources/materials/topologica.mat")->createInstance();
        matInst->setParam("baseColor", vec4(1.f, 0.f, 0.f, 1.f));
        matInst->setParam("specColor", vec4(0.f, 0.f, 1.f, 1.f));
        m_cube->setMaterialInst(matInst);
        Render::getRoot()->addChild(m_cube);
    }

    virtual void onTouch(const event::Touch::Begin&) override
    {
    }

    void onUpdate(float dt) override
    {
        m_cube->rotate(Rotator{dt,dt,dt});
    }

private:
    sptr<Mesh> m_cube;
};

W4_RUN(ShadertoyDemo)

This is enough to retrieve a red cube with blue lighting:

Material_02.png

Adding a complex shader

After you made sure that everything works smoothly, you can switch to the shader from www.shadertoy.com. Its contents are below:

/*--------------------------------------------------------------------------------------
License CC0 - http://creativecommons.org/publicdomain/zero/1.0/
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
----------------------------------------------------------------------------------------
^ This means do ANYTHING YOU WANT with this code. Because we are programmers, not lawyers.
-Otavio Good
*/

// various noise functions
float Hash2d(vec2 uv)
{
    float f = uv.x + uv.y * 47.0;
    return fract(cos(f*3.333)*100003.9);
}
float Hash3d(vec3 uv)
{
    float f = uv.x + uv.y * 37.0 + uv.z * 521.0;
    return fract(cos(f*3.333)*100003.9);
}
float mixP(float f0, float f1, float a)
{
    return mix(f0, f1, a*a*(3.0-2.0*a));
}
const vec2 zeroOne = vec2(0.0, 1.0);
float noise2d(vec2 uv)
{
    vec2 fr = fract(uv.xy);
    vec2 fl = floor(uv.xy);
    float h00 = Hash2d(fl);
    float h10 = Hash2d(fl + zeroOne.yx);
    float h01 = Hash2d(fl + zeroOne);
    float h11 = Hash2d(fl + zeroOne.yy);
    return mixP(mixP(h00, h10, fr.x), mixP(h01, h11, fr.x), fr.y);
}
float noise2dT(vec2 uv)
{
    vec2 fr = fract(uv);
    vec2 smoothv = fr*fr*(3.0-2.0*fr);
    vec2 fl = floor(uv);
    uv = smoothv + fl;
    return textureLod(iChannel0, (uv + 0.5)/iChannelResolution[0].xy, 0.0).y;	// use constant here instead?
}
float noise(vec3 uv)
{
    vec3 fr = fract(uv.xyz);
    vec3 fl = floor(uv.xyz);
    float h000 = Hash3d(fl);
    float h100 = Hash3d(fl + zeroOne.yxx);
    float h010 = Hash3d(fl + zeroOne.xyx);
    float h110 = Hash3d(fl + zeroOne.yyx);
    float h001 = Hash3d(fl + zeroOne.xxy);
    float h101 = Hash3d(fl + zeroOne.yxy);
    float h011 = Hash3d(fl + zeroOne.xyy);
    float h111 = Hash3d(fl + zeroOne.yyy);
    return mixP(
        mixP(mixP(h000, h100, fr.x), mixP(h010, h110, fr.x), fr.y),
        mixP(mixP(h001, h101, fr.x), mixP(h011, h111, fr.x), fr.y)
        , fr.z);
}

float PI=3.14159265;

vec3 saturate(vec3 a)
{
	return clamp(a, 0.0, 1.0);
}
vec2 saturate(vec2 a)
{
	return clamp(a, 0.0, 1.0);
}
float saturate(float a)
{
	return clamp(a, 0.0, 1.0);
}

float Density(vec3 p)
{
    //float ws = 0.06125*0.125;
    //vec3 warp = vec3(noise(p*ws), noise(p*ws + 111.11), noise(p*ws + 7111.11));
    float final = noise(p*0.06125);// + sin(iTime)*0.5-1.95 + warp.x*4.0;
    float other = noise(p*0.06125 + 1234.567);
    other -= 0.5;
    final -= 0.5;
    final = 0.1/(abs(final*final*other));
    final += 0.5;
    return final*0.0001;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	// ---------------- First, set up the camera rays for ray marching ----------------
	vec2 uv = fragCoord.xy/iResolution.xy * 2.0 - 1.0;// - 0.5;

	// Camera up vector.
	vec3 camUp=vec3(0,1,0); // vuv

	// Camera lookat.
	vec3 camLookat=vec3(0,0.0,0);	// vrp

	float mx=iMouse.x/iResolution.x*PI*2.0 + iTime * 0.01;
	float my=-iMouse.y/iResolution.y*10.0 + sin(iTime * 0.03)*0.2+0.2;//*PI/2.01;
	vec3 camPos=vec3(cos(my)*cos(mx),sin(my),cos(my)*sin(mx))*(200.2); 	// prp

	// Camera setup.
	vec3 camVec=normalize(camLookat - camPos);//vpn
	vec3 sideNorm=normalize(cross(camUp, camVec));	// u
	vec3 upNorm=cross(camVec, sideNorm);//v
	vec3 worldFacing=(camPos + camVec);//vcv
	vec3 worldPix = worldFacing + uv.x * sideNorm * (iResolution.x/iResolution.y) + uv.y * upNorm;//scrCoord
	vec3 relVec = normalize(worldPix - camPos);//scp

	// --------------------------------------------------------------------------------
	float t = 0.0;
	float inc = 0.02;
	float maxDepth = 70.0;
	vec3 pos = vec3(0,0,0);
    float density = 0.0;
	// ray marching time
    for (int i = 0; i < 37; i++)	// This is the count of how many times the ray actually marches.
    {
        if ((t > maxDepth)) break;
        pos = camPos + relVec * t;
        float temp = Density(pos);
        //temp *= saturate(t-1.0);

        inc = 1.9 + temp*0.05;	// add temp because this makes it look extra crazy!
        density += temp * inc;
        t += inc;
    }

	// --------------------------------------------------------------------------------
	// Now that we have done our ray marching, let's put some color on this.
	vec3 finalColor = vec3(0.01,0.1,1.0)* density*0.2;

	// output the final color with sqrt for "gamma correction"
	fragColor = vec4(sqrt(clamp(finalColor, 0.0, 1.0)),1.0);
}

Fragment Shader Updating

Copy the shader from the site to the fragment shader of the project, adjusting some names of parameters. All functions are copied, except for the following:

  • mainImage as it will be copied on later stage;
  • noise2dT as it will is not used and leads to an error.

As a result, topologica.fs looks the following way:

uniform vec4 baseColor;
uniform vec4 specColor;

varying vec3 v_normal;
varying vec3 v_toView;
varying vec3 v_toLight;

// various noise functions
float Hash2d(vec2 uv)
{
    float f = uv.x + uv.y * 47.0;
    return fract(cos(f*3.333)*100003.9);
}
float Hash3d(vec3 uv)
{
    float f = uv.x + uv.y * 37.0 + uv.z * 521.0;
    return fract(cos(f*3.333)*100003.9);
}
float mixP(float f0, float f1, float a)
{
    return mix(f0, f1, a*a*(3.0-2.0*a));
}
const vec2 zeroOne = vec2(0.0, 1.0);
float noise2d(vec2 uv)
{
    vec2 fr = fract(uv.xy);
    vec2 fl = floor(uv.xy);
    float h00 = Hash2d(fl);
    float h10 = Hash2d(fl + zeroOne.yx);
    float h01 = Hash2d(fl + zeroOne);
    float h11 = Hash2d(fl + zeroOne.yy);
    return mixP(mixP(h00, h10, fr.x), mixP(h01, h11, fr.x), fr.y);
}
float noise(vec3 uv)
{
    vec3 fr = fract(uv.xyz);
    vec3 fl = floor(uv.xyz);
    float h000 = Hash3d(fl);
    float h100 = Hash3d(fl + zeroOne.yxx);
    float h010 = Hash3d(fl + zeroOne.xyx);
    float h110 = Hash3d(fl + zeroOne.yyx);
    float h001 = Hash3d(fl + zeroOne.xxy);
    float h101 = Hash3d(fl + zeroOne.yxy);
    float h011 = Hash3d(fl + zeroOne.xyy);
    float h111 = Hash3d(fl + zeroOne.yyy);
    return mixP(
        mixP(mixP(h000, h100, fr.x), mixP(h010, h110, fr.x), fr.y),
        mixP(mixP(h001, h101, fr.x), mixP(h011, h111, fr.x), fr.y)
        , fr.z);
}

float PI=3.14159265;

vec3 saturate(vec3 a)
{
	return clamp(a, 0.0, 1.0);
}
vec2 saturate(vec2 a)
{
	return clamp(a, 0.0, 1.0);
}
float saturate(float a)
{
	return clamp(a, 0.0, 1.0);
}

float Density(vec3 p)
{
    //float ws = 0.06125*0.125;
    //vec3 warp = vec3(noise(p*ws), noise(p*ws + 111.11), noise(p*ws + 7111.11));
    float final = noise(p*0.06125);// + sin(iTime)*0.5-1.95 + warp.x*4.0;
    float other = noise(p*0.06125 + 1234.567);
    other -= 0.5;
    final -= 0.5;
    final = 0.1/(abs(final*final*other));
    final += 0.5;
    return final*0.0001;
}

void w4_main()
{
    const float specPower = 10.0;
    vec3 n2 = normalize ( v_normal );
    vec3 v2 = normalize ( v_toView );
    vec3 r  = reflect   ( -v2, n2 );
    vec3 l2 = normalize ( v_toLight );
    vec4 diff = baseColor * max ( dot ( n2, l2 ), 0.0 );
    vec4 spec = specColor * pow ( max ( dot ( l2, r ), 0.0 ), specPower );
    gl_FragColor = vec4(diff.rgb, 1.);
    gl_FragColor.rgb += spec.rgb;
}

The program with the above shader runs without errors, but the cube has not changed, as the mainImage part is missing.

Copy the contents of mainImage to the main of fragment shader and you will get the following code:

uniform vec4 baseColor;
uniform vec4 specColor;

varying vec3 v_normal;
varying vec3 v_toView;
varying vec3 v_toLight;

// various noise functions
float Hash2d(vec2 uv)
{
    float f = uv.x + uv.y * 47.0;
    return fract(cos(f*3.333)*100003.9);
}
float Hash3d(vec3 uv)
{
    float f = uv.x + uv.y * 37.0 + uv.z * 521.0;
    return fract(cos(f*3.333)*100003.9);
}
float mixP(float f0, float f1, float a)
{
    return mix(f0, f1, a*a*(3.0-2.0*a));
}
const vec2 zeroOne = vec2(0.0, 1.0);
float noise2d(vec2 uv)
{
    vec2 fr = fract(uv.xy);
    vec2 fl = floor(uv.xy);
    float h00 = Hash2d(fl);
    float h10 = Hash2d(fl + zeroOne.yx);
    float h01 = Hash2d(fl + zeroOne);
    float h11 = Hash2d(fl + zeroOne.yy);
    return mixP(mixP(h00, h10, fr.x), mixP(h01, h11, fr.x), fr.y);
}

float noise(vec3 uv)
{
    vec3 fr = fract(uv.xyz);
    vec3 fl = floor(uv.xyz);
    float h000 = Hash3d(fl);
    float h100 = Hash3d(fl + zeroOne.yxx);
    float h010 = Hash3d(fl + zeroOne.xyx);
    float h110 = Hash3d(fl + zeroOne.yyx);
    float h001 = Hash3d(fl + zeroOne.xxy);
    float h101 = Hash3d(fl + zeroOne.yxy);
    float h011 = Hash3d(fl + zeroOne.xyy);
    float h111 = Hash3d(fl + zeroOne.yyy);
    return mixP(
        mixP(mixP(h000, h100, fr.x), mixP(h010, h110, fr.x), fr.y),
        mixP(mixP(h001, h101, fr.x), mixP(h011, h111, fr.x), fr.y)
        , fr.z);
}

float PI=3.14159265;

vec3 saturate(vec3 a)
{
	return clamp(a, 0.0, 1.0);
}
vec2 saturate(vec2 a)
{
	return clamp(a, 0.0, 1.0);
}
float saturate(float a)
{
	return clamp(a, 0.0, 1.0);
}

float Density(vec3 p)
{
    //float ws = 0.06125*0.125;
    //vec3 warp = vec3(noise(p*ws), noise(p*ws + 111.11), noise(p*ws + 7111.11));
    float final = noise(p*0.06125);// + sin(iTime)*0.5-1.95 + warp.x*4.0;
    float other = noise(p*0.06125 + 1234.567);
    other -= 0.5;
    final -= 0.5;
    final = 0.1/(abs(final*final*other));
    final += 0.5;
    return final*0.0001;
}

void w4_main()
{
    	// ---------------- First, set up the camera rays for ray marching ----------------
    	vec2 uv = fragCoord.xy/iResolution.xy * 2.0 - 1.0;// - 0.5;

    	// Camera up vector.
    	vec3 camUp=vec3(0,1,0); // vuv

    	// Camera lookat.
    	vec3 camLookat=vec3(0,0.0,0);	// vrp

    	float mx=iMouse.x/iResolution.x*PI*2.0 + iTime * 0.01;
    	float my=-iMouse.y/iResolution.y*10.0 + sin(iTime * 0.03)*0.2+0.2;//*PI/2.01;
    	vec3 camPos=vec3(cos(my)*cos(mx),sin(my),cos(my)*sin(mx))*(200.2); 	// prp

    	// Camera setup.
    	vec3 camVec=normalize(camLookat - camPos);//vpn
    	vec3 sideNorm=normalize(cross(camUp, camVec));	// u
    	vec3 upNorm=cross(camVec, sideNorm);//v
    	vec3 worldFacing=(camPos + camVec);//vcv
    	vec3 worldPix = worldFacing + uv.x * sideNorm * (iResolution.x/iResolution.y) + uv.y * upNorm;//scrCoord
    	vec3 relVec = normalize(worldPix - camPos);//scp

    	// --------------------------------------------------------------------------------
    	float t = 0.0;
    	float inc = 0.02;
    	float maxDepth = 70.0;
    	vec3 pos = vec3(0,0,0);
        float density = 0.0;
    	// ray marching time
        for (int i = 0; i < 37; i++)	// This is the count of how many times the ray actually marches.
        {
            if ((t > maxDepth)) break;
            pos = camPos + relVec * t;
            float temp = Density(pos);
            //temp *= saturate(t-1.0);

            inc = 1.9 + temp*0.05;	// add temp because this makes it look extra crazy!
            density += temp * inc;
            t += inc;
        }

    	// --------------------------------------------------------------------------------
    	// Now that we have done our ray marching, let's put some color on this.
    	vec3 finalColor = vec3(0.01,0.1,1.0)* density*0.2;

    	// output the final color with sqrt for "gamma correction"
    	fragColor = vec4(sqrt(clamp(finalColor, 0.0, 1.0)),1.0);

        const float specPower = 10.0;
        vec3 n2 = normalize ( v_normal );
        vec3 v2 = normalize ( v_toView );
        vec3 r  = reflect   ( -v2, n2 );
        vec3 l2 = normalize ( v_toLight );
        vec4 diff = fragColor * max ( dot ( n2, l2 ), 0.0 );
        vec4 spec = specColor * pow ( max ( dot ( l2, r ), 0.0 ), specPower );
        gl_FragColor = vec4(diff.rgb, 1.);
        gl_FragColor.rgb += spec.rgb;
}

Debugging

If you run the program now, errors will appear with the missing variables: fragCoord, iResolution, iTime and iMouse variables. Let's review them case by case:

1) fragCoord and iResolution are used to calculate uv. uv can be retrieved from the vertex shader (v_uv). Do not forget to complete the headers of both shaders with lines accordingly:

 varying vec2 v_uv;

and add the following to the vertex shader:

attribute vec2 w4_a_uv0;
...
v_uv = w4_a_uv0;

iTime is the time attribute. Add it to the fragment shader:

uniform float w4_u_time;

replace iTime with w4_u_time.

Hardcode iMouse (for later use).

vec2 iMouse = vec2(0, 0);

iResolution is also used to calculate the aspect ratio. To optimize the execution, let's calculate it in the vertex shader.

uniform vec2 w4_u_resolution;
...
varying float v_aspect;
...
v_aspect = w4_u_resolution.x / w4_u_resolution.y;

Note. Since baseColor is no longer used, don't forget to remove it from the shader and source code.

Result

The resulting shaders:

topologica.vs

attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv0;

uniform mat4 projectionView;
uniform mat4 model;
uniform mat3 normalSpace;
uniform vec3 eyePosition;
uniform vec2 resolution;

varying vec3 v_toView;
varying vec3 v_normal;
varying vec3 v_toLight;
varying vec2 v_uv;
varying float v_aspect;

void main()
{
    v_uv = uv0;

    const vec3 lightPosition = vec3(0., 10., -10.);
    gl_Position = projectionView * model * vec4(position , 1.0);
    v_normal  = normalSpace * normal;
    v_toView  = eyePosition - gl_Position.xyz;
    v_toLight = lightPosition - gl_Position.xyz;
    v_aspect = resolution.x / resolution.y;
}

topologica.fs

uniform vec4 specColor;
uniform float w4_u_time;

varying vec3 v_normal;
varying vec3 v_toView;
varying vec3 v_toLight;
varying vec2 v_uv;
varying float v_aspect;

// various noise functions
float Hash2d(vec2 uv)
{
    float f = uv.x + uv.y * 47.0;
    return fract(cos(f*3.333)*100003.9);
}
float Hash3d(vec3 uv)
{
    float f = uv.x + uv.y * 37.0 + uv.z * 521.0;
    return fract(cos(f*3.333)*100003.9);
}
float mixP(float f0, float f1, float a)
{
    return mix(f0, f1, a*a*(3.0-2.0*a));
}
const vec2 zeroOne = vec2(0.0, 1.0);
float noise2d(vec2 uv)
{
    vec2 fr = fract(uv.xy);
    vec2 fl = floor(uv.xy);
    float h00 = Hash2d(fl);
    float h10 = Hash2d(fl + zeroOne.yx);
    float h01 = Hash2d(fl + zeroOne);
    float h11 = Hash2d(fl + zeroOne.yy);
    return mixP(mixP(h00, h10, fr.x), mixP(h01, h11, fr.x), fr.y);
}

float noise(vec3 uv)
{
    vec3 fr = fract(uv.xyz);
    vec3 fl = floor(uv.xyz);
    float h000 = Hash3d(fl);
    float h100 = Hash3d(fl + zeroOne.yxx);
    float h010 = Hash3d(fl + zeroOne.xyx);
    float h110 = Hash3d(fl + zeroOne.yyx);
    float h001 = Hash3d(fl + zeroOne.xxy);
    float h101 = Hash3d(fl + zeroOne.yxy);
    float h011 = Hash3d(fl + zeroOne.xyy);
    float h111 = Hash3d(fl + zeroOne.yyy);
    return mixP(
        mixP(mixP(h000, h100, fr.x), mixP(h010, h110, fr.x), fr.y),
        mixP(mixP(h001, h101, fr.x), mixP(h011, h111, fr.x), fr.y)
        , fr.z);
}

float PI=3.14159265;

vec3 saturate(vec3 a)
{
	return clamp(a, 0.0, 1.0);
}
vec2 saturate(vec2 a)
{
	return clamp(a, 0.0, 1.0);
}
float saturate(float a)
{
	return clamp(a, 0.0, 1.0);
}

float Density(vec3 p)
{
    //float ws = 0.06125*0.125;
    //vec3 warp = vec3(noise(p*ws), noise(p*ws + 111.11), noise(p*ws + 7111.11));
    float final = noise(p*0.06125);// + sin(iTime)*0.5-1.95 + warp.x*4.0;
    float other = noise(p*0.06125 + 1234.567);
    other -= 0.5;
    final -= 0.5;
    final = 0.1/(abs(final*final*other));
    final += 0.5;
    return final*0.0001;
}

void w4_main()
{
    	// ---------------- First, set up the camera rays for ray marching ----------------
    	// Camera up vector.
    	vec3 camUp=vec3(0,1,0); // vuv

    	// Camera lookat.
    	vec3 camLookat=vec3(0,0.0,0);	// vrp

        vec2 iMouse = vec2(0, 0);
        vec2 iResolution = vec2(1, 1);

    	float mx=iMouse.x/iResolution.x*PI*2.0 + w4_u_time * 0.01;
    	float my=-iMouse.y/iResolution.y*10.0 + sin(w4_u_time * 0.03)*0.2+0.2;//*PI/2.01;
    	vec3 camPos=vec3(cos(my)*cos(mx),sin(my),cos(my)*sin(mx))*(200.2); 	// prp

    	// Camera setup.
    	vec3 camVec=normalize(camLookat - camPos);//vpn
    	vec3 sideNorm=normalize(cross(camUp, camVec));	// u
    	vec3 upNorm=cross(camVec, sideNorm);//v
    	vec3 worldFacing=(camPos + camVec);//vcv
    	vec3 worldPix = worldFacing + v_uv.x * sideNorm * v_aspect + v_uv.y * upNorm;//scrCoord
    	vec3 relVec = normalize(worldPix - camPos);//scp

    	// --------------------------------------------------------------------------------
    	float t = 0.0;
    	float inc = 0.02;
    	float maxDepth = 70.0;
    	vec3 pos = vec3(0,0,0);
        float density = 0.0;
    	// ray marching time
        for (int i = 0; i < 37; i++)	// This is the count of how many times the ray actually marches.
        {
            if ((t > maxDepth)) break;
            pos = camPos + relVec * t;
            float temp = Density(pos);
            //temp *= saturate(t-1.0);

            inc = 1.9 + temp*0.05;	// add temp because this makes it look extra crazy!
            density += temp * inc;
            t += inc;
        }

    	// --------------------------------------------------------------------------------
    	// Now that we have done our ray marching, let's put some color on this.
    	vec3 finalColor = vec3(0.01,0.1,1.0)* density*0.2;

    	// output the final color with sqrt for "gamma correction"
    	vec4 fragColor = vec4(sqrt(clamp(finalColor, 0.0, 1.0)),1.0);

        const float specPower = 10.0;
        vec3 n2 = normalize ( v_normal );
        vec3 v2 = normalize ( v_toView );
        vec3 r  = reflect   ( -v2, n2 );
        vec3 l2 = normalize ( v_toLight );
        vec4 diff = fragColor * max ( dot ( n2, l2 ), 0.0 );
        vec4 spec = specColor * pow ( max ( dot ( l2, r ), 0.0 ), specPower );
        gl_FragColor = vec4(diff.rgb, 1.);
        gl_FragColor.rgb += spec.rgb;
}

sample.cpp has remained unchanged, except for the deleted line with baseColor.

Let's run the program:

Material_03.png

As a result of the previous steps the material is added.

Cursor handler

The mouse position can simply be removed from the shader, but in our case we keep it. This can be done by adding a mouse movement handler and passing the normalized cursor position to the shader.

The resulting code: sample.cpp

#include "W4Framework.h"

W4_USE_UNSTRICT_INTERFACE

struct ShadertoyDemo : public IGame
{
public:
    void onStart() override
    {
        m_cube = Mesh::create::cube({2.f, 2.f, 2.f});
        m_matInst = Material::get("resources/materials/topologica.mat")->createInstance();
        m_matInst->setParam("specColor", vec4(0.f, 0.f, 1.f, 1.f));
        m_cube->setMaterialInst(m_matInst);
        Render::getRoot()->addChild(m_cube);

        event::Touch::Move::subscribe(std::bind(&ShadertoyDemo::onMove, this, std::placeholders::_1));
    }

    void onMove(const event::Touch::Move& evt)
    {
        const auto& screenSize = Platform::instance().getSize();
        m_matInst->setParam("mousePos", vec2(static_cast<float>(evt.point.x) / screenSize.w, static_cast<float>(evt.point.y) / screenSize.h));
    }

    void onUpdate(float dt) override
    {
        m_cube->rotate(Rotator{dt,dt,dt});
    }

private:
    sptr<Mesh> m_cube;
    sptr<MaterialInst> m_matInst;
};

W4_RUN(ShadertoyDemo)

topologica.fs

uniform vec4 specColor;
uniform vec2 mousePos;

varying vec3 v_normal;
varying vec3 v_toView;
varying vec3 v_toLight;
varying vec2 v_uv;
varying float v_aspect;

// various noise functions
float Hash2d(vec2 uv)
{
    float f = uv.x + uv.y * 47.0;
    return fract(cos(f*3.333)*100003.9);
}
float Hash3d(vec3 uv)
{
    float f = uv.x + uv.y * 37.0 + uv.z * 521.0;
    return fract(cos(f*3.333)*100003.9);
}
float mixP(float f0, float f1, float a)
{
    return mix(f0, f1, a*a*(3.0-2.0*a));
}
const vec2 zeroOne = vec2(0.0, 1.0);
float noise2d(vec2 uv)
{
    vec2 fr = fract(uv.xy);
    vec2 fl = floor(uv.xy);
    float h00 = Hash2d(fl);
    float h10 = Hash2d(fl + zeroOne.yx);
    float h01 = Hash2d(fl + zeroOne);
    float h11 = Hash2d(fl + zeroOne.yy);
    return mixP(mixP(h00, h10, fr.x), mixP(h01, h11, fr.x), fr.y);
}

float noise(vec3 uv)
{
    vec3 fr = fract(uv.xyz);
    vec3 fl = floor(uv.xyz);
    float h000 = Hash3d(fl);
    float h100 = Hash3d(fl + zeroOne.yxx);
    float h010 = Hash3d(fl + zeroOne.xyx);
    float h110 = Hash3d(fl + zeroOne.yyx);
    float h001 = Hash3d(fl + zeroOne.xxy);
    float h101 = Hash3d(fl + zeroOne.yxy);
    float h011 = Hash3d(fl + zeroOne.xyy);
    float h111 = Hash3d(fl + zeroOne.yyy);
    return mixP(
        mixP(mixP(h000, h100, fr.x), mixP(h010, h110, fr.x), fr.y),
        mixP(mixP(h001, h101, fr.x), mixP(h011, h111, fr.x), fr.y)
        , fr.z);
}

float PI=3.14159265;

vec3 saturate(vec3 a)
{
	return clamp(a, 0.0, 1.0);
}
vec2 saturate(vec2 a)
{
	return clamp(a, 0.0, 1.0);
}
float saturate(float a)
{
	return clamp(a, 0.0, 1.0);
}

float Density(vec3 p)
{
    //float ws = 0.06125*0.125;
    //vec3 warp = vec3(noise(p*ws), noise(p*ws + 111.11), noise(p*ws + 7111.11));
    float final = noise(p*0.06125);// + sin(iTime)*0.5-1.95 + warp.x*4.0;
    float other = noise(p*0.06125 + 1234.567);
    other -= 0.5;
    final -= 0.5;
    final = 0.1/(abs(final*final*other));
    final += 0.5;
    return final*0.0001;
}

void w4_main()
{
    	// ---------------- First, set up the camera rays for ray marching ----------------
    	// Camera up vector.
    	vec3 camUp=vec3(0,1,0); // vuv

    	// Camera lookat.
    	vec3 camLookat=vec3(0,0.0,0);	// vrp

    	float mx=mousePos.x*PI*2.0 + w4_u_time * 0.01;
    	float my=-mousePos.y*10.0 + sin(w4_u_time * 0.03)*0.2+0.2;//*PI/2.01;
    	vec3 camPos=vec3(cos(my)*cos(mx),sin(my),cos(my)*sin(mx))*(200.2); 	// prp

    	// Camera setup.
    	vec3 camVec=normalize(camLookat - camPos);//vpn
    	vec3 sideNorm=normalize(cross(camUp, camVec));	// u
    	vec3 upNorm=cross(camVec, sideNorm);//v
    	vec3 worldFacing=(camPos + camVec);//vcv
    	vec3 worldPix = worldFacing + v_uv.x * sideNorm * v_aspect + v_uv.y * upNorm;//scrCoord
    	vec3 relVec = normalize(worldPix - camPos);//scp

    	// --------------------------------------------------------------------------------
    	float t = 0.0;
    	float inc = 0.02;
    	float maxDepth = 70.0;
    	vec3 pos = vec3(0,0,0);
        float density = 0.0;
    	// ray marching w4_u_time
        for (int i = 0; i < 37; i++)	// This is the count of how many times the ray actually marches.
        {
            if ((t > maxDepth)) break;
            pos = camPos + relVec * t;
            float temp = Density(pos);
            //temp *= saturate(t-1.0);

            inc = 1.9 + temp*0.05;	// add temp because this makes it look extra crazy!
            density += temp * inc;
            t += inc;
        }

    	// --------------------------------------------------------------------------------
    	// Now that we have done our ray marching, let's put some color on this.
    	vec3 finalColor = vec3(0.01,0.1,1.0)* density*0.2;

    	// output the final color with sqrt for "gamma correction"
    	vec4 fragColor = vec4(sqrt(clamp(finalColor, 0.0, 1.0)),1.0);

        const float specPower = 10.0;
        vec3 n2 = normalize ( v_normal );
        vec3 v2 = normalize ( v_toView );
        vec3 r  = reflect   ( -v2, n2 );
        vec3 l2 = normalize ( v_toLight );
        vec4 diff = fragColor * max ( dot ( n2, l2 ), 0.0 );
        vec4 spec = specColor * pow ( max ( dot ( l2, r ), 0.0 ), specPower );
        gl_FragColor = vec4(diff.rgb, 1.);
        gl_FragColor.rgb += spec.rgb;
}

The vertex shader has remained unchanged.

Run the program:

http://demo.w4-dev.ciliz.com/wiki-files/Material_final.mov

When you move the mouse with the pressed button, the shader reacts similarly to the shadertoy implementation. Therefore, the functionality is fully transferred!