Color effect

All topics about ZGameEditor goes here.

Moderator: Moderators

Post Reply
User avatar
rrTea
Posts: 475
Joined: Sat Feb 15, 2014 9:54 am

Color effect

Post by rrTea »

I'm thinking of implementing an effect that would make the whole screen look as if it were rendered in 8 bit palette. The effect should look something like in the attached screenshot - note the glitchy effects on the background etc. This particular example was done by just recording the screen and reducing it to 256 colors. Ideally there could also be some dithering effects and varying color fidelity for a slightly "gif-y' effect.
https://imgur.com/wuGbA2r

How would I go about this? Right now I draw the screen using one single RenderTarget, so I assume the next step would be to plug a shader into the Material that I use to draw the RenderTarget texture? Or?...
Last edited by rrTea on Thu Mar 22, 2018 10:11 am, edited 1 time in total.
User avatar
Kjell
Posts: 1882
Joined: Sat Feb 23, 2008 11:15 pm

Re: Color effect

Post by Kjell »

Hi rrTea,

Basically yes .. but there quite a few different dithering algorithms ( each with their own specific look ). Attached are two simple examples; one that simply reduces the output to 256 colors / 8-bit ( red and green are 3-bit while blue is 2-bit ) and another one that dithers the output using ordered dithering ( Bayer matrix ).

By the way, i'd strongly recommend not going this route. Your game is already "difficult to read" at times due to the low resolution. Reducing the color-depth would only make this worse ( in my opinion ).

K
Attachments
256.zgeproj
(2.08 KiB) Downloaded 441 times
Dither.zgeproj
(3.8 KiB) Downloaded 460 times
User avatar
rrTea
Posts: 475
Joined: Sat Feb 15, 2014 9:54 am

Re: Color effect

Post by rrTea »

Gave it a quick test - it works! I tried using it in my current project - actually looks pretty good... for the most part ;) (but there are some things that turn into a mess). Right now I don't really understand how it works so I'll have to study it a bit before trying to (eventually) use it seriously. Still, good to know this is how it's done, thanks!
User avatar
Kjell
Posts: 1882
Joined: Sat Feb 23, 2008 11:15 pm

Re: Color effect

Post by Kjell »

Hi rrTea,
rrTea wrote:Right now I don't really understand how it works so I'll have to study it a bit before trying to (eventually) use it seriously.
The way that it works is that the screen is divided up in blocks of 8x8 pixels ( using modulo ). Each color component of those pixels are compared against a value from a 8x8 dithering threshold matrix ( you can find these values on Wikipedia ). So when you want 3-bit depth ( just like red and green in the example ), you multiply those components by 7 ( 3-bit ranges from 0 to 7 ), use the floor as the base-value and test whether the remaining fraction is above or below the dithering threshold for that pixel.

K
User avatar
rrTea
Posts: 475
Joined: Sat Feb 15, 2014 9:54 am

Re: Color effect

Post by rrTea »

Yes, I thought so - after a bit of testing it seems this approach can't work well for my particular situation without significant tweakings.

The actual game itself looks almost the same but without even going into the whole readability issue, some things look almost unrecognizable, especially the parts where lots of gradients are used (the banding takes over the whole image - cutscenes etc).

Too bad, some parts look very interesting rendered like this, especially various fadings. I'll try to use this for some other things.
User avatar
Ats
Posts: 616
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Color effect

Post by Ats »

All right, I managed to convert the dither example to ES2/GL3:

Code: Select all

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

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

  Box.Rotation.X = rnd();
  Box.Rotation.Y = rnd();
  Box.Rotation.Z = rnd();

  Box.RotationVelocity.X = random(0, 0.125);
  Box.RotationVelocity.Y = random(0, 0.125);

  createModel(Box);
}]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnBeginRenderPass>
    <SetRenderTarget RenderTarget="Canvas"/>
  </OnBeginRenderPass>
  <OnRender>
    <SetRenderTarget RenderTarget="Dither"/>
    <UseMaterial Material="CanvasMaterial"/>
    <RenderMesh Mesh="ScreenMesh"/>
    <SetRenderTarget/>
    <UseMaterial Material="DitherMaterial"/>
    <ZExpression>
      <Expression>
<![CDATA[//

mat4 m;

for(int i=0; i<4; i++)
{
  m[i,i] = 1;
}

setMatrix(0, m);
setMatrix(1, m);]]>
      </Expression>
    </ZExpression>
    <RenderMesh Mesh="ScreenMesh"/>
  </OnRender>
  <Content>
    <Model Name="Box" Position="1.2733 3.8833 -2.6916" Rotation="0.5828 0.543 0.018" RotationVelocity="0.0719 0.1196 0">
      <OnRender>
        <UseMaterial Material="BoxMaterial"/>
        <RenderMesh Mesh="BoxMesh"/>
      </OnRender>
    </Model>
    <Mesh Name="BoxMesh">
      <Producers>
        <MeshBox/>
        <MeshExpression AutoNormals="0" VertexColors="255">
          <Expression>
<![CDATA[//

C.R = V.R;
C.G = V.G;
C.B = V.B;]]>
          </Expression>
        </MeshExpression>
      </Producers>
    </Mesh>
    <Material Name="BoxMaterial" Shader="BasicShader"/>
    <Array Name="Bayer" Type="4" Dimensions="1" SizeDim1="8" SizeDim2="8" Persistent="255">
      <Values>
<![CDATA[789C6350E0D06052E2D23210B0903012B292E2D16151E1D36353B3913111B19333136356E6D66654E4D43416B6963614B494E4D76757E7D56555B5973717B79535150500F23007E1]]>
      </Values>
    </Array>
    <Bitmap Name="BayerBitmap" Width="8" Height="8" Filter="1">
      <Producers>
        <BitmapExpression>
          <Expression>
<![CDATA[//

Pixel.R = Bayer[X*7,Y*7]/64.0;]]>
          </Expression>
        </BitmapExpression>
      </Producers>
    </Bitmap>
    <RenderTarget Name="Canvas" Width="1" Height="1" Filter="1"/>
    <Mesh Name="ScreenMesh">
      <Producers>
        <MeshBox/>
      </Producers>
    </Mesh>
    <Shader Name="CanvasShader">
      <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 vec2 texCoord; // texture coordinates

varying vec2 v_texCoord;

void main()
{
  gl_Position = position;
  v_texCoord = texCoord;
}]]>
      </VertexShaderSource>
      <FragmentShaderSource>
<![CDATA[
#ifdef GL_ES
  precision mediump float;
#endif

uniform sampler2D tex1;
uniform sampler2D tex2;

varying vec2 v_texCoord; // To receive texture coordinates from the vertex shader

float getColor(float x, float y, float c) {
    float limit = 0.0;
    if (x < 1.0) {
        limit = texture2D(tex1, vec2(x, y)).r;
    }
    return c <= limit ? 0.0 : 1.0;
}

void main() {
    vec3 rgb = texture2D(tex2, v_texCoord).rgb;

    vec2 coord = gl_FragCoord.xy;

    float x = mod(coord.x, 8.0) / 8.0;
    float y = mod(coord.y, 8.0) / 8.0;

    vec3 rgbDithered;

    rgbDithered.r = floor(rgb.r * 7.0) / 7.0 + getColor(x, y, fract(rgb.r * 7.0)) / 7.0;
    rgbDithered.g = floor(rgb.g * 7.0) / 7.0 + getColor(x, y, fract(rgb.g * 7.0)) / 7.0;
    rgbDithered.b = floor(rgb.b * 3.0) / 3.0 + getColor(x, y, fract(rgb.b * 3.0)) / 3.0;

    gl_FragColor = vec4(rgbDithered, 1.0);
}]]>
      </FragmentShaderSource>
    </Shader>
    <Material Name="CanvasMaterial" Shading="1" Light="0" ZBuffer="0" Shader="CanvasShader">
      <Textures>
        <MaterialTexture Texture="BayerBitmap" TextureWrapMode="2" TexCoords="1"/>
        <MaterialTexture RenderTarget="Canvas" TextureWrapMode="2" TexCoords="1"/>
      </Textures>
    </Material>
    <RenderTarget Name="Dither" Width="1" Height="1" Filter="1"/>
    <Material Name="DitherMaterial" Shading="1" Light="0" Shader="TextureShader">
      <Textures>
        <MaterialTexture RenderTarget="Dither" TextureWrapMode="2" TexCoords="1"/>
      </Textures>
    </Material>
    <Shader Name="TextureShader">
      <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 vec2 texCoord; // texture coordinates

varying vec2 t;

void main()
{
  gl_Position = position;
  t = texCoord;
}]]>
      </VertexShaderSource>
      <FragmentShaderSource>
<![CDATA[//
#ifdef GL_ES
  precision mediump float;
#endif

uniform sampler2D tex1;
varying vec2 t;

void main()
{
  gl_FragColor = texture2D(tex1, t);
}]]>
      </FragmentShaderSource>
    </Shader>
    <Shader Name="BasicShader">
      <VertexShaderSource>
<![CDATA[//
#ifdef GL_ES
  precision mediump float;
#endif

varying vec4 colorVar;

//The variables below are predefined in ZGE
uniform mat4 modelViewProjectionMatrix;
attribute vec3 position;    //vertex position
attribute vec4 color;       //vertex color

void main()
{
  colorVar = color;

  // project the transformed position
  vec4 p = modelViewProjectionMatrix * vec4(position, 1.0);
  gl_Position = p;
}]]>
      </VertexShaderSource>
      <FragmentShaderSource>
<![CDATA[//
#ifdef GL_ES
  precision mediump float;
#endif

varying vec4 colorVar;

void main() {
  gl_FragColor = colorVar;
}]]>
      </FragmentShaderSource>
    </Shader>
  </Content>
</ZApplication>
I mean, it's working, but I'm not sure if I wrote the shaders correctly. Likewise, there might be a few unneeded lines :lol:
Also, why isn't working only in fullscreen + desktop resolution? (black screen)

Edit: mmm... Original example isn't working in fullscreen + desktop resolution either :|
Post Reply