Scrolling water shader

All topics about ZGameEditor goes here.

Moderator: Moderators

Post Reply
User avatar
Ats
Posts: 791
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Scrolling water shader

Post by Ats »

Hello, for a change of Android bug hunting, I converted this Godot water shader by Verssales to ZGE. I'm pretty happy with how it went :lol:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" GLBase="1" ClearScreenMode="1" CameraPosition="0 1 4" CameraRotation="0.02 0 0" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <BeforeInitExp>
<![CDATA[//

if (ANDROID)
{
  if(App.GLBase==0) this.ModuleName="libGLESv1_CM.so";
  else this.ModuleName="libGLESv2.so";
}
else if (LINUX)
{
  this.ModuleName = "libGL.so";
}
else
{
  this.ModuleName = "opengl32";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[const int GL_DEPTH_BUFFER_BIT = 0x0100;
const int GL_COLOR_BUFFER_BIT = 0x4000;

void glClear(int mode){}]]>
      </Source>
    </ZExternalLibrary>
    <ZExpression>
      <Expression>
<![CDATA[time = 0;

WaterColor[0][0,0] = 0.3;
WaterColor[0][0,1] = 0.3;
WaterColor[0][0,2] = 0.6;]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Time += App.DeltaTime;

float t = App.Time;
LightPosition[0][0,0] = cos(t) * 50;
LightPosition[0][0,1] = 60 + cos(t*0.1)*10;
LightPosition[0][0,2] = 100;

LightColorAmbient[0][0,0] = 0.5+cos(t*2)*0.02;
LightColorAmbient[0][0,1] = 0.5+cos(t)*0.02;
LightColorAmbient[0][0,2] = 0.5+sin(t*3)*0.02;]]>
      </Expression>
    </ZExpression>
  </OnUpdate>
  <OnRender>
    <UseMaterial Material="MaterialWater"/>
    <RenderMesh Mesh="MeshWater"/>
  </OnRender>
  <Content>
    <Variable Name="Time"/>
    <Array Name="WaterColor" Type="5" SizeDim1="1"/>
    <Array Name="LightPosition" Type="5" SizeDim1="1"/>
    <Array Name="LightColorAmbient" Type="5" SizeDim1="1"/>
    <Array Name="FogColor" Type="5" SizeDim1="1"/>
    <Mesh Name="MeshWater">
      <Producers>
        <MeshBox Scale="10 10 1" XCount="32" YCount="32" Grid2DOnly="255"/>
        <MeshTransform Rotation="-0.25 0 0"/>
      </Producers>
    </Mesh>
    <Material Name="MaterialWater" Shading="1" Shader="ShaderWater"/>
    <Shader Name="ShaderWater" UpdateVarsOnEachUse="255">
      <VertexShaderSource>
<![CDATA[//
#ifdef GL_ES
  #ifdef GL_FRAGMENT_PRECISION_HIGH
    precision highp float;
  #else
    precision mediump float;
  #endif
#endif

attribute vec4 position;  // Vertex position
attribute vec3 normal;    // Vertex normal

uniform float time;
uniform mat4 modelViewProjectionMatrix;
uniform mat4 modelViewMatrix;  // For transforming normals
uniform mat3 normalMatrix;     // To properly transform normals

varying vec3 v_position; // Position in view space


float generateOffset(float x, float y, float val1, float val2, float time) {
  float amount = 0.2;
  float speed = 0.4;

  float radiansX = ((mod(x + y * x * val1, amount) / amount) + (time * speed) * mod(x * 0.8 + y, 1.5)) * 2.0 * 3.14159;
  float radiansY = ((mod(val2 * (y * x + x * y), amount) / amount) + (time * speed) * 2.0 * mod(x, 2.0)) * 2.0 * 3.14159;

  return amount * 0.5 * (sin(radiansY) * cos(radiansX));
}

vec3 applyDistortion(vec3 vertex, float time) {
  float xd = generateOffset(vertex.x, vertex.z, 0.2, 0.1, time);
  float yd = generateOffset(vertex.x, vertex.z, 0.1, 0.3, time);
  float zd = generateOffset(vertex.x, vertex.z, 0.15, 0.2, time);
  return vertex + vec3(xd, yd, zd);
}

void main() {
  vec3 distortedPosition = applyDistortion(vec3(position.x, position.y, position.z), time * 0.1);

  // Transform position and normal
  v_position = vec3(modelViewMatrix * vec4(distortedPosition, 1.0)); // View-space position

  // Transform the position into clip space
  gl_Position = modelViewProjectionMatrix * vec4(distortedPosition, 1.0);
}]]>
      </VertexShaderSource>
      <FragmentShaderSource>
<![CDATA[#ifdef GL_ES
  precision mediump float;
#endif

uniform mat4 waterColor;  // Water color
//uniform float beer_factor;

uniform mat4 lightPosition;  // Light position in view space
uniform mat4 lightColorAmbient;     // Light color
uniform mat4 fogColor;   // Ambient light color

varying vec3 v_position; // Interpolated position from the vertex shader

void main() {
  vec3 dPosition_dx = dFdx(v_position);
  vec3 dPosition_dy = dFdy(v_position);
  vec3 recalculatedNormal = normalize(cross(dPosition_dx, dPosition_dy));


  // Light position
  vec3 LPosition = vec3(lightPosition[0][0], lightPosition[0][1], lightPosition[0][2]);
  vec3 L = normalize(LPosition - v_position);

  vec4 globalColor = vec4(waterColor[0][0], waterColor[0][1], waterColor[0][2], 1.0);

  // calculate Diffuse Term
  vec4 LAmbient = vec4(lightColorAmbient[0][0], lightColorAmbient[0][1], lightColorAmbient[0][2], 1.0);
  vec4 LDiffuse = globalColor * LAmbient;

  // clamp pour éviter les couleurs brulées sur android avec la distance de la lumière
  vec4 Idiff = 3.0 * LDiffuse * max(dot(recalculatedNormal,L), 0.0);

  // Fog
  vec4 fog = vec4(fogColor[0][0], fogColor[0][1], fogColor[0][2], 1.0);

  // Final color
  vec4 color = Idiff;

 gl_FragColor = mix(color, fog, clamp(-v_position.z / 500.0, 0.0, 1.0));
}]]>
      </FragmentShaderSource>
      <UniformVariables>
        <ShaderVariable VariableName="time" VariableRef="Time"/>
        <ShaderVariable VariableName="waterColor" ValueArrayRef="WaterColor" ArrayKind="1"/>
        <ShaderVariable VariableName="lightColorAmbient" ValueArrayRef="LightColorAmbient" ArrayKind="1"/>
        <ShaderVariable VariableName="lightPosition" ValueArrayRef="LightPosition" ArrayKind="1"/>
        <ShaderVariable VariableName="fogColor" ValueArrayRef="FogColor" ArrayKind="1"/>
      </UniformVariables>
    </Shader>
  </Content>
</ZApplication>
But now I am wondering how to scroll it... Like a scrolling texture. Is there a known technique for that?
Everything I've found is about scrolling textures.
User avatar
Kjell
Posts: 1921
Joined: Sat Feb 23, 2008 11:15 pm

Re: Scrolling water shader

Post by Kjell »

Hi Ats,
Ats wrote: Wed Nov 20, 2024 5:02 pmBut now I am wondering how to scroll it... Like a scrolling texture. Is there a known technique for that?
Not entirely sure what type of scrolling effect you're trying to achieve .. but perhaps it's either one of the following:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="Scroll" CameraPosition="0 3 3" CameraRotation="0.125 0 0" LightPosition="0 1 0.5" FileVersion="2">
  <OnLoaded>
    <ZExpression>
      <Expression>
<![CDATA[// Spawn a bunch of boxes for camera movement reference

for(int i=0; i<16; i++)
{
  Box.Position.X = rnd() < 0.5 ? 2.5 : -2.5;
  Box.Position.Z = -i;

  createModel(Box);
}]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[// Move camera forward

App.CameraPosition.Z = 3-App.Time;

// Set values for "static" mesh

StaticScroll.Value = floor(-App.Time*8)/8;
StaticTransform.Translate.Z = StaticScroll.Value;

// Set values for "dynamic" mesh

DynamicScroll.Value = -App.Time;]]>
      </Expression>
    </ZExpression>
  </OnUpdate>
  <OnRender>
    <RenderTransformGroup Name="StaticTransform" Translate="-1 0 0">
      <Children>
        <UseMaterial Material="StaticMaterial"/>
        <RenderMesh Mesh="DemoMesh"/>
      </Children>
    </RenderTransformGroup>
    <RenderTransformGroup Translate="1 0 0">
      <Children>
        <UseMaterial Material="DynamicMaterial"/>
        <RenderMesh Mesh="DemoMesh"/>
      </Children>
    </RenderTransformGroup>
  </OnRender>
  <Content>
    <Mesh Name="DemoMesh">
      <Producers>
        <MeshBox XCount="15" YCount="15" Grid2DOnly="255"/>
        <MeshTransform Rotation="0.75 0 0"/>
      </Producers>
    </Mesh>
    <Bitmap Name="DemoBitmap">
      <Producers>
        <BitmapNoise Octaves="2" Color="1" Tile="255"/>
        <BitmapExpression>
          <Expression>
<![CDATA[//

Pixel.R = (Pixel.R-0.5)*8+1;]]>
          </Expression>
        </BitmapExpression>
      </Producers>
    </Bitmap>
    <Shader Name="StaticShader" UpdateVarsOnEachUse="255">
      <VertexShaderSource>
<![CDATA[//

uniform sampler2D tex1;
uniform float scroll;
varying float color;

void main()
{
  vec4 vertex = gl_Vertex;
  vertex.y = texture2D(tex1, vertex.xz + vec2(0.0, scroll)).r / 8.0;
  color = vertex.y * 16.0;
  gl_Position = gl_ModelViewProjectionMatrix * vertex;
}]]>
      </VertexShaderSource>
      <FragmentShaderSource>
<![CDATA[//

varying float color;

void main()
{
  gl_FragColor = vec4(0.0, color * 0.35, color, 1.0);
}]]>
      </FragmentShaderSource>
      <UniformVariables>
        <ShaderVariable Name="StaticScroll" VariableName="scroll"/>
      </UniformVariables>
    </Shader>
    <Material Name="StaticMaterial" Shading="2" Light="0" Shader="StaticShader">
      <Textures>
        <MaterialTexture Texture="DemoBitmap" TextureWrapMode="1" TexCoords="1"/>
      </Textures>
    </Material>
    <Shader Name="DynamicShader" UpdateVarsOnEachUse="255">
      <VertexShaderSource>
<![CDATA[//

uniform sampler2D tex1;
uniform float scroll;
varying float color;

void main()
{
  vec4 vertex = gl_Vertex;
  vertex.z += scroll;
  vertex.y = texture2D(tex1, vertex.xz).r / 8.0;
  color = vertex.y * 16.0;
  gl_Position = gl_ModelViewProjectionMatrix * vertex;
}]]>
      </VertexShaderSource>
      <FragmentShaderSource>
<![CDATA[//

varying float color;

void main()
{
  gl_FragColor = vec4(0.0, color * 0.35, color, 1.0);
}]]>
      </FragmentShaderSource>
      <UniformVariables>
        <ShaderVariable Name="DynamicScroll" VariableName="scroll"/>
      </UniformVariables>
    </Shader>
    <Material Name="DynamicMaterial" Shading="2" Light="0" Shader="DynamicShader">
      <Textures>
        <MaterialTexture Texture="DemoBitmap" TextureWrapMode="1" TexCoords="1"/>
      </Textures>
    </Material>
    <Model Name="Box" Position="2.5 0 -15">
      <OnRender>
        <UseMaterial Material="BoxMaterial"/>
        <RenderMesh Mesh="BoxMesh"/>
      </OnRender>
    </Model>
    <Mesh Name="BoxMesh">
      <Producers>
        <MeshBox Scale="0.1 1 0.1"/>
        <MeshTransform Position="0 1 0"/>
      </Producers>
    </Mesh>
    <Material Name="BoxMaterial" Shading="1"/>
  </Content>
</ZApplication>
I'm using a static ( opposed to dynamic ) bitmap in this example on purpose just so you can easily see the difference between the two methods. Obviously you're calculating the ( dynamic ) deformation in your vertex shader instead of using a texture, but the principle remains the same.

K
User avatar
Ats
Posts: 791
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Scrolling water shader

Post by Ats »

Cool :o
The use of a moving mesh over a static bitmap is very nice. But after a few tests, I didn't manage to move the vertices the way I want. The only solution I found was to scramble the bitmap and call RefreshComponent at every frame. But I think I'll use this for lava.

I made a working 2D version of what I'm trying to achieve with the shader:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZLibrary HasInitializer="1">
      <Source>
<![CDATA[float time = 0;
float horizontalOffset = 0;
int ballz = 20;

float fmod(float a, float N) {return a - N*floor(a/N);}]]>
      </Source>
    </ZLibrary>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[time = App.Time;

float xStart = 11;
float yStart = 0;
float speed = 0.4; // speed of plane over sea
float waveAmplitude = 2;
float waveSpeed =  0.2;
float wavePeriod = 0.9;
float dotSpacing = 0.5;

ballz = 50;
ArrayBallz.SizeDim1 = ballz;
RepeatDraw.Count = ballz;

//horizontalOffset = (horizontalOffset + speed) % dotSpacing; // Modulo isn't working with float?
horizontalOffset = fmod(horizontalOffset + speed, dotSpacing);

float phaseOffset = (horizontalOffset / dotSpacing) * PI*2 / ballz;

// Update positions of all the balls
for (int i=0; i<ballz; i++) {
	float phase = phaseOffset + (PI*2 / ballz) * i;
	ArrayBallz[i].x = xStart - wavePeriod * dotSpacing * i - horizontalOffset;
  ArrayBallz[i].y = yStart + waveAmplitude * sin(time * waveSpeed * PI * 2 + phase);
}]]>
      </Expression>
    </ZExpression>
  </OnUpdate>
  <OnRender>
    <Repeat Name="RepeatDraw" Count="50" WhileExp="return this.Iteration &lt; ballz;">
      <OnIteration>
        <ZExpression>
          <Expression>
<![CDATA[TransformBall.Translate.X = ArrayBallz[RepeatDraw.Iteration].X;
TransformBall.Translate.Y = ArrayBallz[RepeatDraw.Iteration].Y;]]>
          </Expression>
        </ZExpression>
        <RenderTransformGroup Name="TransformBall" Scale="0.2 0.2 0.2" Translate="-11.15 0.2171 0">
          <Children>
            <RenderMesh Mesh="MeshBall"/>
          </Children>
        </RenderTransformGroup>
      </OnIteration>
    </Repeat>
  </OnRender>
  <Content>
    <Array Name="ArrayBallz" Type="6" SizeDim1="50"/>
    <Mesh Name="MeshBall">
      <Producers>
        <MeshSphere ZSamples="3" RadialSamples="5"/>
      </Producers>
    </Mesh>
  </Content>
</ZApplication>
All the variables are located in OnUpdate, so I could easily tweak them.
Now I'm trying to adapt that to the shader, so throbing vertices of the waves are advancing on the z axis, while the mesh itself isn't moving.

Oh, and I have a question: why isn't modulo % working with float?
Post Reply