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?...
Color effect
Moderator: Moderators
Color effect
Last edited by rrTea on Thu Mar 22, 2018 10:11 am, edited 1 time in total.
Re: Color effect
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
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 581 times
-
- Dither.zgeproj
- (3.8 KiB) Downloaded 605 times
Re: Color effect
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!
Re: Color effect
Hi rrTea,
K
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.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.
K
Re: Color effect
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.
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.
Re: Color effect
All right, I managed to convert the dither example to ES2/GL3:
I mean, it's working, but I'm not sure if I wrote the shaders correctly. Likewise, there might be a few unneeded lines
Also, why isn't working only in fullscreen + desktop resolution? (black screen)
Edit: mmm... Original example isn't working in fullscreen + desktop resolution either
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>
Also, why isn't working only in fullscreen + desktop resolution? (black screen)
Edit: mmm... Original example isn't working in fullscreen + desktop resolution either