Questions about ES2/GL3 shaders

All topics about ZGameEditor goes here.

Moderator: Moderators

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

Re: Questions about ES2/GL3 shaders

Post by Ats »

Image
Matrixes... :roll:
Yeah, I saw you complain here and there regarding matrix and shaders already.

Thanks for your example. So I played a bit with it in order to comprehend how it's working. I guess I need to do the same on all three axes, right? But I'm struggling with the Z axis rotation. Then I searched through the forum for other Matrix examples, then I discovered that one could "add from library" by right-clicking the Project Tree in ZGE. That's a neat but really hidden feature... :lol:

So now I'm playing with the Matrix Library, trying to get the correct rotation on all three axes from that.
User avatar
Ats
Posts: 835
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Questions about ES2/GL3 shaders

Post by Ats »

I managed to get a correct rotation transformation on all three axes with the Matrix Library. I had to multiply the matrices in a certain order (I'm discovering matrices here).

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" ClearColor="0.5 0.5 0.5 1" CameraPosition="-7.2006 2 3.4859" CameraRotation="0 1.1782 0" RenderOrder="1" FileVersion="2">
  <OnLoaded>
    <ZLibrary Comment="Matrix Library">
      <Source>
<![CDATA[// Returns a identity matrix.

mat4 matrixIdentity()
{
  mat4 m;

  m[0,0] = 1;
  m[1,1] = 1;
  m[2,2] = 1;
  m[3,3] = 1;

  return m;
}

// Returns a translation matrix.

mat4 matrixFromTranslation(vec3 translation)
{
  mat4 m;

  m[0,0] = 1;
  m[1,1] = 1;
  m[2,2] = 1;
  m[3,3] = 1;

  m[3,0] = translation.x;
  m[3,1] = translation.y;
  m[3,2] = translation.z;

  return m;
}

// Returns a scale matrix.

mat4 matrixFromScale(vec3 scale)
{
  mat4 m;

  m[0,0] = scale.x;
  m[1,1] = scale.y;
  m[2,2] = scale.z;
  m[3,3] = 1;

  return m;
}

// Returns a x-axis rotation matrix.

mat4 matrixFromRotationX(float angle)
{
  angle *= PI*2;

  float s = sin(angle);
  float c = cos(angle);

  mat4 m;

  m[1,1] = c; m[2,1] = 0-s;
  m[1,2] = s; m[2,2] = c;

  m[0,0] = 1;
  m[3,3] = 1;

  return m;
}

// Returns a y-axis rotation matrix.

mat4 matrixFromRotationY(float angle)
{
  angle *= PI*2;

  float s = sin(angle);
  float c = cos(angle);

  mat4 m;

  m[0,0] = c;   m[2,0] = s;
  m[0,2] = 0-s; m[2,2] = c;

  m[1,1] = 1;
  m[3,3] = 1;

  return m;
}

// Returns a z-axis rotation matrix.

mat4 matrixFromRotationZ(float angle)
{
  angle *= PI*2;

  float s = sin(angle);
  float c = cos(angle);

  mat4 m;

  m[0,0] = c; m[1,0] = 0-s;
  m[0,1] = s; m[1,1] = c;

  m[2,2] = 1;
  m[3,3] = 1;

  return m;
}

// Returns a euler-angles matrix.

mat4 matrixFromEuler(vec3 rotation)
{
  float sx, cx, sy, cy, sz, cz;

  // Would have been nice if you could do "rotation *= PI*2" :-(
  rotation.x *= PI*2;
  rotation.y *= PI*2;
  rotation.z *= PI*2;

  sx = sin(rotation.x);
  cx = cos(rotation.x);

  sy = sin(rotation.y);
  cy = cos(rotation.y);

  sz = sin(rotation.z);
  cz = cos(rotation.z);

  mat4 m;

  m[0,0] = cy*cz; m[1,0] = sx*cz*sy-cx*sz; m[2,0] = cx*cz*sy+sx*sz;
  m[0,1] = cy*sz; m[1,1] = sx*sz*sy+cx*cz; m[2,1] = cx*sz*sy-sx*cz;
  m[0,2] = 0-sy;  m[1,2] = sx*cy;          m[2,2] = cx*cy;

  m[3,3] = 1;

  return m;
}

// Returns a axis-angle matrix.

mat4 matrixFromAxisAngle(vec3 axis, float angle)
{
  float s, c, x, y, z, t;

  angle *= PI*2;

  s = sin(angle);
  c = cos(angle);

  x = axis.x;
  y = axis.y;
  z = axis.z;

  t = 1-c;

  mat4 m;

  m[0,0] = t*x*x+c;   m[1,0] = t*x*y-s*z; m[2,0] = t*x*z+s*y;
  m[0,1] = t*x*y+s*z; m[1,1] = t*y*y+c;   m[2,1] = t*y*z-s*x;
  m[0,2] = t*x*z-s*y; m[1,2] = t*y*z+s*x; m[2,2] = t*z*z+c;

  m[3,3] = 1;

  return m;
}

// Returns a quaternion matrix.

mat4 matrixFromQuaternion(vec4 quaternion)
{
  float x, y, z, w, x2, y2, z2;

  x = quaternion.x;
  y = quaternion.y;
  z = quaternion.z;
  w = quaternion.w;

  x2 = x+x;
  y2 = y+y;
  z2 = z+z;

  mat4 m;

  m[0,0] = 1-(y*y2+z*z2); m[1,0] =    x*y2-w*z2;  m[2,0] =    x*z2+w*y2;
  m[0,1] =    x*y2-w*z2;  m[1,1] = 1-(x*x2+z*z2); m[2,1] =    y*z2-w*x2;
  m[0,2] =    x*z2-w*y2;  m[1,2] =    y*z2+w*x2;  m[2,2] = 1-(x*x2+y*y2);

  m[3,3] = 1;

  return m;
}

// Returns a model matrix.

mat4 matrixFromModel(model q)
{
  // Would have been nice if you could do "mat4 m = matrixFromEuler(q.rotation)" :-(
  vec3 rotation = vector3(q.rotation.x, q.rotation.y, q.rotation.z);
  mat4 m = matrixFromEuler(rotation);

  m[3,0] = q.position.x;
  m[3,1] = q.position.y;
  m[3,2] = q.position.z;

  for(int i=0; i<3; i++)
  {
    m[0,i] *= q.scale.x;
    m[1,i] *= q.scale.y;
    m[2,i] *= q.scale.z;
  }

  return m;
}]]>
      </Source>
    </ZLibrary>
    <SpawnModel Model="Box"/>
  </OnLoaded>
  <OnRender>
    <ZExpression>
      <Expression>
<![CDATA[// Get the current modelViewMatrix ( which contains the viewMatrix at this point )

getMatrix(0, ViewMatrix);]]>
      </Expression>
    </ZExpression>
  </OnRender>
  <Content>
    <Variable Name="ViewMatrix" Type="5"/>
    <Model Name="Box">
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[// Animate the box

Box.Position.Y = abs(sin(App.Time))*2.5+1;
Box.Position.X = sin(App.Time)*2;
Box.Position.Z = cos(App.Time)*2;
Box.Rotation.X = App.Time/PI;
Box.Rotation.Y = App.Time/PI;
Box.Rotation.Z = App.Time/PI;]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <UseMaterial Material="BoxMaterial"/>
        <RenderMesh Mesh="BoxMesh"/>
        <ZExpression>
          <Expression>
<![CDATA[
mat4 mt = matrixFromTranslation(vector3(CurrentModel.Position.X, 0, CurrentModel.Position.Z));
mat4 ms = matrixFromScale(vector3(1,0,1));
mat4 mx = matrixFromRotationX(CurrentModel.Rotation.X);
mat4 my = matrixFromRotationY(CurrentModel.Rotation.Y);
mat4 mz = matrixFromRotationZ(CurrentModel.Rotation.Z);
setMatrix(0, ViewMatrix * mt * ms * mz * my * mx);]]>
          </Expression>
        </ZExpression>
        <RenderMesh Mesh="BoxMesh"/>
      </OnRender>
    </Model>
    <Mesh Name="BoxMesh">
      <Producers>
        <MeshBox/>
        <MeshBox Scale="2 0.1 0.1"/>
        <MeshCombine/>
        <MeshBox Scale="0.4 1.5 0.4"/>
        <MeshCombine/>
      </Producers>
    </Mesh>
    <Material Name="BoxMaterial" Shading="2" Light="0"/>
  </Content>
</ZApplication>
Now I'm going to try with what Kjell sent me :wink:
User avatar
Ats
Posts: 835
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Questions about ES2/GL3 shaders

Post by Ats »

I’m totally into shaders now, so I have a few more questions :lol:

1. How can I obtain a unique number per model in a shader?
I tried vec3 modelSeed = position.xyz; but it’s not enough. Can I pass a random number from the currently rendered model to its shader?

2. I’ve noticed sometimes shaders take time to display. Maybe they take time to compile or compute, I’m not sure. So I was wondering: do they stay in memory once they’ve been used? If that’s the case, can I precompute them at the start of the game and reuse them later without delay?
User avatar
VilleK
Site Admin
Posts: 2387
Joined: Mon Jan 15, 2007 4:50 pm
Location: Stockholm, Sweden
Contact:

Re: Questions about ES2/GL3 shaders

Post by VilleK »

Ats wrote: Wed Jul 02, 2025 3:53 pm Can I pass a random number from the currently rendered model to its shader?
You could pass a value using the ShaderVariable. The Shader needs to have the UpdateVarsOnEachUse property set to make sure the variables update for each model.
Ats wrote: Wed Jul 02, 2025 3:53 pm 2. I’ve noticed sometimes shaders take time to display.
This sounds like shader compilation. It is compiled the first time it is used. I guess the only way around that is to render something small using each shader during application initialisation.
User avatar
Kjell
Posts: 1937
Joined: Sat Feb 23, 2008 11:15 pm

Re: Questions about ES2/GL3 shaders

Post by Kjell »

Hi Ats,
Ats wrote: Wed Jul 02, 2025 3:53 pmCan I pass a random number from the currently rendered model to its shader?
That's what uniform variables are for :wink: Unfortunately, there's a issue with ShaderVariable. Since UseMaterial is ignored when the selected material is already active, any changes to ShaderVariable will be skipped in that case too ( regardless of UpdateVarsOnEachUse ). You can work around this by temporarily switching to a dummy material, like this:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZExpression>
      <Expression>
<![CDATA[// Spawn 4 models

for(int i=0; i<4; i++)
{
  Sprite.Position.X = random(0, 4);
  Sprite.Position.Y = random(0, 2);

  createModel(Sprite);
}]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <Content>
    <Material Name="DummyMaterial"/>
    <Model Name="Sprite">
      <OnRender>
        <ZExpression>
          <Expression>
<![CDATA[//

SpriteRandom.Value = Sprite.Personality;]]>
          </Expression>
        </ZExpression>
        <UseMaterial Material="DummyMaterial"/>
        <UseMaterial Material="SpriteMaterial"/>
        <RenderSprite/>
      </OnRender>
    </Model>
    <Material Name="SpriteMaterial" Shader="SpriteShader"/>
    <Shader Name="SpriteShader" UpdateVarsOnEachUse="255">
      <VertexShaderSource>
<![CDATA[//

void main()
{
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}]]>
      </VertexShaderSource>
      <FragmentShaderSource>
<![CDATA[//

uniform float random;

void main()
{
  gl_FragColor = vec4(random, 1-random, 1, 1);
}]]>
      </FragmentShaderSource>
      <UniformVariables>
        <ShaderVariable Name="SpriteRandom" VariableName="random" Value="0.554"/>
      </UniformVariables>
    </Shader>
  </Content>
</ZApplication>
But for your specific situation you might want to use a glUniform call instead, for example:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <Source>
<![CDATA[//

void glUniform1f(int location, float v0){}]]>
      </Source>
    </ZExternalLibrary>
    <ZExpression>
      <Expression>
<![CDATA[// Spawn 4 models

for(int i=0; i<4; i++)
{
  Sprite.Position.X = random(0, 4);
  Sprite.Position.Y = random(0, 2);

  createModel(Sprite);
}]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <Content>
    <Model Name="Sprite">
      <OnRender>
        <UseMaterial Material="SpriteMaterial"/>
        <ZExpression>
          <Expression>
<![CDATA[// Modify uniform value of active shader

glUniform1f(0, Sprite.Personality);]]>
          </Expression>
        </ZExpression>
        <RenderSprite/>
      </OnRender>
    </Model>
    <Material Name="SpriteMaterial" Shader="SpriteShader"/>
    <Shader Name="SpriteShader">
      <VertexShaderSource>
<![CDATA[//

void main()
{
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}]]>
      </VertexShaderSource>
      <FragmentShaderSource>
<![CDATA[//

uniform float random;

void main()
{
  gl_FragColor = vec4(random, 1-random, 1, 1);
}]]>
      </FragmentShaderSource>
    </Shader>
  </Content>
</ZApplication>
You can get the uniform location using glGetUniformLocation ( use Shader.Handle as program argument ).

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

Re: Questions about ES2/GL3 shaders

Post by Ats »

VilleK wrote:I guess the only way around that is to render something small using each shader during application initialisation.
All right, I'll add that to my arcade style warming up screen :wink:
Kjell wrote:But for your specific situation you might want to use a glUniform call instead
Thanks, that’s perfect. I managed to use it in my model-independent per-vertex position modifier. What I don’t understand is how glUniform1f sets a random value inside the shader. The documentation says “glUniform - Specify the value of a uniform variable for the current program object.” So does it set the seed for the current iteration of the shader, and any uniform variables are affected?
User avatar
Kjell
Posts: 1937
Joined: Sat Feb 23, 2008 11:15 pm

Re: Questions about ES2/GL3 shaders

Post by Kjell »

Hi Ats,
Ats wrote: Wed Jul 02, 2025 8:58 pmWhat I don’t understand is how glUniform1f sets a random value inside the shader.
The glUniform function(s) set the value of a specific uniform variable of the shader that is currently active. It's the "location" argument that determines which uniform you're writing to. In the example i posted my shader only has one ( user-defined ) uniform variable, so the location should be 0 ( just like the first element of an array ).

In other words; glUniform1f(0, Sprite.Personality) basically does "SpriteShader.random = Sprite.Personality".

Generally i'd recommended calling glGetUniformLocation(MyShader.Handle, "myUniform") once a shader is compiled, and then storing that location value for later use .. but sometimes a shortcut doesn't hurt :wink:

K
Post Reply