Help for wireframe in Android ES2/GL3

All topics about ZGameEditor goes here.

Moderator: Moderators

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

Help for wireframe in Android ES2/GL3

Post by Ats »

Hello. I'm trying to write a shader to display the edges of a model, just like the basic Material Wireframe, but that should work on Android.
I'm getting to something, but the lines are in the middle of the faces, not on the edges. And I'm kind of lost :lol:

What is wrong with the way I'm doing this?

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" GLBase="1" FileVersion="2">
  <OnLoaded>
    <SpawnModel Model="Model_Box"/>
  </OnLoaded>
  <Content>
    <Model Name="Model_Box">
      <OnRender>
        <UseMaterial Material="Material_Wire"/>
        <RenderMesh Mesh="Mesh_Box"/>
      </OnRender>
    </Model>
    <Mesh Name="Mesh_Box">
      <Producers>
        <MeshBox/>
        <MeshExpression VertexColors="255">
          <Expression>
<![CDATA[//V : current vertex
//N : current normal (turn off AutoNormals when modifying normals)
//C : current color (turn on VertexColors)
//TexCoord : current texture coordinate (turn on HasTexCoords)
C.R = V.X;
C.G = V.Y;
C.B = -V.X;]]>
          </Expression>
        </MeshExpression>
      </Producers>
    </Mesh>
    <Material Name="Material_Wire" Shader="Shader_Wire"/>
    <Shader Name="Shader_Wire">
      <VertexShaderSource>
<![CDATA[
#ifdef GL_ES
  #ifdef GL_FRAGMENT_PRECISION_HIGH
    precision highp float;
  #else
    precision mediump float;
  #endif
#endif

attribute vec4 position;
attribute vec4 color;

uniform mat4 modelViewProjectionMatrix;

varying vec4 v_color;
varying vec3 v_pos;

void main()
{
  v_color = color;
  v_pos = position.xyz;
  gl_Position = modelViewProjectionMatrix * position;
}
  ]]>
      </VertexShaderSource>
      <FragmentShaderSource>
<![CDATA[
#ifdef GL_ES
  precision mediump float;
  //#extension GL_OES_standard_derivatives : enable
#endif

varying vec4 v_color;
varying vec3 v_pos;

float edgeFactor(vec3 p)
{
  vec3 d = fwidth(p);
  vec3 a3 = smoothstep(vec3(0.0), d * 10.0, p);
  return min(min(a3.x, a3.y), a3.z);
}

void main()
{
  float edge = edgeFactor(abs(normalize(v_pos)));
  vec3 fillColor = vec3(0.0);
  vec3 lineColor = v_color.rgb;
  float mixVal = smoothstep(0.0, 0.1, edge); // edge softness
  vec3 finalColor = mix(lineColor, fillColor, mixVal);
  gl_FragColor = vec4(finalColor, 1.0);
}]]>
      </FragmentShaderSource>
    </Shader>
  </Content>
</ZApplication>
Last edited by Ats on Mon Oct 13, 2025 12:24 pm, edited 2 times in total.
User avatar
Kjell
Posts: 1948
Joined: Sat Feb 23, 2008 11:15 pm

Re: Help for edge shader (wireframe)

Post by Kjell »

Hi Ats,

Looks like you're trying to use barycentric coordinates to determine the pixel distance to edge? In that case .. you're accidentally using the vertex position as edge factor input, while you should be using your barycentric coordinates / vertex colors.

Below is a simple example ( for regular OpenGL ):

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" ClearColor="0.7529 0.7529 0.7529 1" FileVersion="2">
  <OnLoaded>
    <SpawnModel Model="Box"/>
  </OnLoaded>
  <Content>
    <Model Name="Box" RotationVelocity="0.02 0.03 0">
      <OnRender>
        <UseMaterial Material="BoxMaterial"/>
        <RenderMesh Mesh="BoxMesh"/>
      </OnRender>
    </Model>
    <Variable Name="BoxIndex" Type="1"/>
    <Material Name="BoxMaterial" Light="0" Shader="BoxShader"/>
    <Mesh Name="BoxMesh">
      <Producers>
        <MeshBox/>
        <ZExpression Expression="BoxIndex = 0;"/>
        <MeshExpression AutoNormals="0" VertexColors="255">
          <Expression>
<![CDATA[switch(BoxIndex++ & 3)
{
  case 0:
  case 3: C = vector4(1, 0, 0, 1); break;
  case 1: C = vector4(0, 1, 0, 1); break;
  case 2: C = vector4(0, 0, 1, 1); break;
}]]>
          </Expression>
        </MeshExpression>
      </Producers>
    </Mesh>
    <Shader Name="BoxShader">
      <VertexShaderSource>
<![CDATA[void main()
{
  gl_FrontColor = gl_Color;
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}]]>
      </VertexShaderSource>
      <FragmentShaderSource>
<![CDATA[void main()
{
  vec3 d = step(fwidth(gl_Color.rgb), gl_Color.rgb);
  gl_FragColor = gl_Color * min(min(d.x, d.y), d.z);
}]]>
      </FragmentShaderSource>
    </Shader>
  </Content>
</ZApplication>
Personally i'm not a huge fan of this approach as it "draws" a line on the inside of a triangle .. resulting in a wireframe that appears thicker on edges that lie in between visible triangles compared to edges that don't.

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

Re: Help for edge shader (wireframe)

Post by Ats »

Oh, thanks. Coloring each triangle with 3 colors was that I was trying to do, but I didn't know I could put a ZExpression inside a Mesh Producers. So I was kind of stuck, wondering if I had to edit the mesh inside blender or something... Your example is really helpful.

I don't seem to find a simple way to draw wireframes in ES2/GL3. I've read about duplicating the 3DS models by suppressing the faces, while keeping the edges. Or using an OpenGL functions to manually draw GL_LINES in between vertices if there is a significative angle for each edge shared by two triangles, but I don't think ES2 has GL_LINES.


Edit:
After reading a bit more, I could simply use geometry shader... Now I'm getting to something :lol:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="Smart wireframe demo" GLBase="1" ClearColor="0.8 0.8 0.8 1" FileVersion="2">
  <OnLoaded>
    <SpawnModel Model="Model_Box"/>
  </OnLoaded>
  <Content>
    <Model Name="Model_Box" RotationVelocity="0.3 0.4 0">
      <OnRender>
        <UseMaterial Material="Mat_Wire"/>
        <RenderMesh Mesh="Mesh_Box"/>
      </OnRender>
    </Model>
    <Mesh Name="Mesh_Box">
      <Producers>
        <MeshBox/>
      </Producers>
    </Mesh>
    <Material Name="Mat_Wire" Shader="Shader_Wire"/>
    <Shader Name="Shader_Wire">
      <VertexShaderSource>
<![CDATA[#ifdef GL_ES
precision mediump float;
#endif

attribute vec4 position;
attribute vec3 normal;
attribute vec4 color;

uniform mat4 modelViewProjectionMatrix;
uniform mat3 normalMatrix;

varying vec4 v_color;

void main() {
    v_color = color; // pass color to fragment
    gl_Position = modelViewProjectionMatrix * position;
}]]>
      </VertexShaderSource>
      <GeometryShaderSource>
<![CDATA[#version 330 core
layout(triangles) in;
layout(line_strip, max_vertices = 6) out;

varying vec4 v_color[];

void main() {
    // For each triangle, emit 3 edges
    for(int i = 0; i < 3; i++) {
        gl_Position = gl_in[i].gl_Position;
        EmitVertex();
        gl_Position = gl_in[(i+1)%3].gl_Position;
        EmitVertex();
        EndPrimitive();
    }
}]]>
      </GeometryShaderSource>
      <FragmentShaderSource>
<![CDATA[#ifdef GL_ES
precision mediump float;
#endif

varying vec4 v_color;

void main() {
    gl_FragColor = v_color; // render edge color
}]]>
      </FragmentShaderSource>
    </Shader>
  </Content>
</ZApplication>
User avatar
Kjell
Posts: 1948
Joined: Sat Feb 23, 2008 11:15 pm

Re: Help for edge shader (wireframe)

Post by Kjell »

Hi Ats,
Ats wrote: Mon Oct 13, 2025 10:05 amColoring each triangle with 3 colors was that I was trying to do, but I didn't know I could put a ZExpression inside a Mesh Producers.
It feels like a bit of a hack. Would have been more convenient if there was a way to access mesh data ( vertices & faces ) directly, or if MeshExpression had a VertexIndex property.
Ats wrote: Mon Oct 13, 2025 10:05 amOr using an OpenGL functions to manually draw GL_LINES in between vertices if there is a significative angle for each edge shared by two triangles, but I don't think ES2 has GL_LINES.
All versions of OpenGL ES support GL_LINES, GL_LINE_LOOP and GL_LINE_STRIP.
Ats wrote: Mon Oct 13, 2025 10:05 amAfter reading a bit more, I could simply use geometry shader...
Do keep in mind that Geometry Shaders only work in OpenGL ES 3.2 .. and that they will always be significantly slower compared to using a static mesh.

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

Re: Help for edge shader (wireframe) in ES2/GL3

Post by Ats »

All versions of OpenGL ES support GL_LINES, GL_LINE_LOOP and GL_LINE_STRIP.
Oh, ok. Reddit is so full of crap :lol:

Anyway, I have something that is working a bit like I want. I'm going to try it on Android, to see if it is showing up. And I'll make it better, faster and stronger another day.

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="Smart wireframe demo" GLBase="1" ClearColor="0.8 0.8 0.8 1" FileVersion="2">
  <OnLoaded>
    <SpawnModel Model="Model_Box"/>
  </OnLoaded>
  <Content>
    <Model Name="Model_Box" RotationVelocity="0.3 0.4 0">
      <OnRender>
        <UseMaterial Material="Mat_Wireframe"/>
        <RenderMesh Mesh="Mesh_Box"/>
      </OnRender>
    </Model>
    <Mesh Name="Mesh_Box">
      <Producers>
        <MeshBox/>
        <MeshExpression AutoNormals="0" VertexColors="255">
          <Expression>
<![CDATA[// Set all vertices to solid green
C.R = V.X;
C.G = -V.X;
C.B = V.Y;
C.A = 1.0;]]>
          </Expression>
        </MeshExpression>
      </Producers>
    </Mesh>
    <Material Name="Mat_Wireframe" Shader="Shader_Wireframe"/>
    <Shader Name="Shader_Wireframe">
      <VertexShaderSource>
<![CDATA[#ifdef GL_ES
precision mediump float;
#endif

attribute vec4 position;
attribute vec3 normal;
attribute vec4 color;

uniform mat4 modelViewProjectionMatrix;
uniform mat3 normalMatrix;

varying vec4 v_color;

void main() {
  v_color = color;
  gl_Position = modelViewProjectionMatrix * position;
}]]>
      </VertexShaderSource>
      <GeometryShaderSource>
<![CDATA[#version 150 core
layout(triangles) in;
layout(line_strip, max_vertices = 6) out;

in vec4 v_color[];
out vec4 f_color;

void main() {
  vec4 wireColor = v_color[0];

  for(int i = 0; i < 3; i++) {
    f_color = wireColor;
    gl_Position = gl_in[i].gl_Position;
    EmitVertex();

    f_color = wireColor;
    gl_Position = gl_in[(i+1)%3].gl_Position;
    EmitVertex();

    EndPrimitive();
  }
}]]>
      </GeometryShaderSource>
      <FragmentShaderSource>
<![CDATA[#ifdef GL_ES
precision mediump float;
#endif

in vec4 f_color;
out vec4 fragColor;

void main() {
  fragColor = f_color;
}]]>
      </FragmentShaderSource>
    </Shader>
  </Content>
</ZApplication>
Edit:
And... It's super broken on Android :roll:
Post Reply