Added preliminary specular lighting to shaders. The "no spotlight" shader has not yet been updated.

This commit is contained in:
Bart Trzynadlowski 2012-02-06 02:54:43 +00:00
parent 9fc0d7b3cc
commit 6814ef1ed8
4 changed files with 425 additions and 354 deletions

View file

@ -22,8 +22,7 @@
#
# Makefile.SDL.Win32.GCC
#
# GNU Makefile for SDL port using GCC (MinGW) on Windows. Copy this Makefile
# to the base directory of the source distribution (one up from Src/ and here).
# GNU Makefile for SDL port using GCC (MinGW) on Windows.
#
@ -89,10 +88,12 @@ OUTFILE = $(BIN_DIR)\Supermodel.exe
###############################################################################
CC = gcc
CXX = g++
LD = g++
COMPILER_FLAGS = -I$(SDL_INCLUDEPATH) -ISrc/ -ISrc/OSD/ -ISrc/OSD/SDL/ -ISrc/OSD/Windows/ -c -Wall -O3 -DSUPERMODEL_WIN32 -DGLEW_STATIC
COMPILER_FLAGS = -I$(SDL_INCLUDEPATH) -ISrc/ -ISrc/OSD/ -ISrc/OSD/SDL/ -ISrc/OSD/Windows/ -c -Wall -O3 -DSUPERMODEL_WIN32 -DGLEW_STATIC
CFLAGS = $(COMPILER_FLAGS)
CPPFLAGS = $(COMPILER_FLAGS)
#LFLAGS = -s -o $(OUTFILE) $(OBJ) -L$(SDL_LIBPATH) -lmingw32 -lSDLmain -lSDL -lopengl32 -lglu32 -ldinput8 -ldxguid -lole32 -loleaut32 -lz -l:$(WINSDK_LIBPATH)/WbemUuid.lib
LFLAGS = -s -o $(OUTFILE) $(OBJ) -L$(SDL_LIBPATH) -lmingw32 -lSDLmain -lSDL -lopengl32 -lglu32 -ldinput8 -ldxguid -lole32 -loleaut32 -lz -l:$(WINSDK_LIBPATH)/WbemUuid.lib
#
@ -188,40 +189,40 @@ $(OBJ_DIR)/m68kopnz.o: $(OBJ_DIR)/m68kopnz.c $(OBJ_DIR)/m68kmake.exe $(OBJ_DIR)/
# Rules for Supermodel C++ code
#
$(OBJ_DIR)/%.o: Src/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/Model3/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/Graphics/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/Graphics/%.cpp Src/Graphics/Shaders2D.h Src/Graphics/Shaders3D.h
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/Sound/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/Debugger/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/Debugger/CPU/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/CPU/PowerPC/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/CPU/PowerPC/%.cpp Src/CPU/PowerPC/ppc.h Src/CPU/PowerPC/ppc603.c Src/CPU/PowerPC/ppc_ops.c Src/CPU/PowerPC/ppc_ops.h
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/CPU/68K/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/CPU/Z80/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/Inputs/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/OSD/SDL/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/OSD/Windows/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/Pkgs/%.c
$(CC) $< $(CFLAGS) -o $(OBJ_DIR)/$(*F).o
@ -232,4 +233,4 @@ $(OBJ_DIR)/%.o: Src/Pkgs/%.c
# To eliminate name conflicts, object files have the prefix "amp_" attached.
#
$(OBJ_DIR)/amp_%.o: Src/Sound/MPEG/%.cpp
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o
$(CXX) $< $(CPPFLAGS) -o $(OBJ_DIR)/amp_$(*F).o

View file

@ -32,14 +32,16 @@ uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
// Inputs from vertex shader
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
varying float fsTexFormat; // .x=T1RGB5 contour texture (if > 0)
varying float fsTexFormat; // T1RGB5 contour texture (if > 0)
varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque)
varying vec3 fsLightIntensity; // lighting intensity
varying float fsFogFactor; // fog factor
varying float fsSpecularTerm; // specular highlight
varying float fsFogFactor; // fog factor
varying float fsViewZ; // Z distance to fragment from viewpoint at origin
/*
@ -82,12 +84,6 @@ vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEna
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;
/*
glTexCoord = ( mirror*(texSize-vec4(1.0,1.0,1.0,1.0)-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;
*/
return glTexCoord;
}
@ -105,10 +101,11 @@ void main(void)
vec2 ellipse;
vec3 lightIntensity;
float insideSpot;
int x;
// Get polygon color for untextured polygons (textured polygons will overwrite)
if (fsTexParams.x==0.0)
fragColor = gl_Color;
if (fsTexParams.x < 0.5)
fragColor = gl_Color;
else
// Textured polygons: set fragment color to texel value
{
@ -163,7 +160,7 @@ void main(void)
* When the alpha value is 0.0 (or close), pixels are discarded
* entirely.
*/
if (fsTexParams.y > 0.0) // contour processing enabled
if (fsTexParams.y > 0.5) // contour processing enabled
{
if (fragColor.a < 0.01) // discard anything with alpha == 0
discard;
@ -182,12 +179,13 @@ void main(void)
else
lightIntensity = fsLightIntensity;
fragColor.rgb *= lightIntensity;
fragColor.rgb += vec3(fsSpecularTerm,fsSpecularTerm,fsSpecularTerm);
// Translucency (modulates existing alpha channel for RGBA4 texels)
fragColor.a *= fsTransLevel;
// Apply fog under the control of fog factor setting from polygon header
fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor );
fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor);
// Store final color
gl_FragColor = fragColor;

View file

@ -34,14 +34,14 @@ uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, light
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
//uniform vec2 texOffset; // offset (within 2048x2048 texture sheet) to apply to texture base coordinates
// Custom vertex attributes
attribute vec4 subTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
attribute vec4 texParams; // .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
attribute float texFormat; // .x=T1RGB5 contour texture (if > 0)
attribute float texFormat; // T1RGB5 contour texture (if > 0)
attribute float transLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value
attribute float lightEnable; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity
attribute float shininess; // specular shininess (if >= 0.0) or disable specular lighting (negative)
attribute float fogIntensity; // fog intensity (1.0, full fog effect, 0.0, no fog)
// Custom outputs to fragment shader
@ -50,8 +50,9 @@ varying vec4 fsTexParams;
varying float fsTexFormat;
varying float fsTransLevel;
varying vec3 fsLightIntensity; // total light intensity for this vertex
varying float fsSpecularTerm; // specular light term (additive)
varying float fsFogFactor; // fog factor
varying float fsViewZ;
varying float fsViewZ;
// Gets the 3x3 matrix out of a 4x4 (because mat3(mat4matrix) does not work on ATI!)
mat3 GetLinearPart( mat4 m )
@ -104,7 +105,7 @@ void main(void)
gl_FrontColor = gl_Color; // untextured polygons will use this
gl_FrontColor.a = 1.0;
fsLightIntensity = vec3(1.0,1.0,1.0);
if (texParams.x > 0.0) // textured
if (texParams.x > 0.5) // textured
fsLightIntensity *= gl_Color.rgb;
/*
@ -113,7 +114,8 @@ void main(void)
* Parallel light source and ambient lighting are only applied for non-
* luminous polygons.
*/
if (lightEnable > 0.5) // not luminous
fsSpecularTerm = 0.0;
if (lightEnable > 0.5) // not luminous
{
// Normal -> view space
viewNormal = normalize(GetLinearPart(modelViewMatrix)*gl_Normal);
@ -125,7 +127,39 @@ void main(void)
sunFactor = max(dot(sunVector,viewNormal),0.0);
// Total light intensity: sum of all components
fsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y);
fsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y);
/*
* Specular Lighting
*
* The specular term is treated similarly to the "separate specular
* color" functionality of OpenGL: it is added as a highlight in the
* fragment shader. This allows even black textures to be lit.
*
* TO-DO: Ambient intensity viewport parameter is known but what about
* the intensity of the specular term? Always applied with full
* intensity here but this is unlikely to be correct.
*/
if (shininess >= 0.0)
{
// Standard specular lighting equation
vec3 V = normalize(-viewVertex);
vec3 H = normalize(sunVector+V); // halfway vector
float s = max(10,64-shininess); // seems to look nice, but probably not correct
fsSpecularTerm = pow(max(dot(viewNormal,H),0),s);
if (sunFactor <= 0) fsSpecularTerm = 0;
// Faster approximation
//float temp = max(dot(viewNormal,H),0);
//float s = 64-shininess;
//fsSpecularTerm = temp/(s-temp*s+temp);
// Phong formula
//vec3 R = normalize(2*dot(sunVector,viewNormal)*viewNormal - sunVector);
//vec3 V = normalize(-viewVertex);
//float s = max(2,64-shininess);
//fsSpecularTerm = pow(max(dot(R,V),0),s);
}
}
// Fog
@ -138,7 +172,6 @@ void main(void)
// Pass remaining parameters to fragment shader
gl_TexCoord[0] = gl_MultiTexCoord0;
fsSubTexture = subTexture;
//fsSubTexture.xy += texOffset; // apply texture offset
fsTexParams = texParams;
fsTransLevel = transLevel;
fsTexFormat = texFormat;

View file

@ -31,148 +31,183 @@
// Vertex shader
static const char vertexShaderSource[] =
{
"/** \n"
" ** Supermodel \n"
" ** A Sega Model 3 Arcade Emulator. \n"
" ** Copyright 2011 Bart Trzynadlowski \n"
" ** \n"
" ** This file is part of Supermodel. \n"
" ** \n"
" ** Supermodel is free software: you can redistribute it and/or modify it under \n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option) \n"
" ** any later version. \n"
" ** \n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT \n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for \n"
" ** more details. \n"
" ** \n"
" ** You should have received a copy of the GNU General Public License along \n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>. \n"
" **/ \n"
"\n"
"/* \n"
" * Vertex.glsl \n"
" * \n"
" * Vertex shader for 3D rendering. \n"
" */ \n"
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011 Bart Trzynadlowski, Nik Henson \n"
" **\n"
" ** This file is part of Supermodel.\n"
" **\n"
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
" ** any later version.\n"
" **\n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
" ** more details.\n"
" **\n"
" ** You should have received a copy of the GNU General Public License along\n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
" **/\n"
" \n"
"/*\n"
" * Vertex.glsl\n"
" *\n"
" * Vertex shader for 3D rendering.\n"
" */\n"
" \n"
"#version 120\n"
"\n"
"#version 120 \n"
"// Global uniforms\n"
"uniform mat4\tmodelViewMatrix;\t// model -> view space matrix\n"
"uniform mat4\tprojectionMatrix;\t// view space -> screen space matrix\n"
"uniform vec3\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
"uniform vec4\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
"uniform vec2\tspotRange;\t\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
"uniform vec3\tspotColor;\t\t\t// spotlight RGB color\n"
"\n"
"// Global uniforms \n"
"uniform mat4 modelViewMatrix; // model -> view space matrix \n"
"uniform mat4 projectionMatrix; // view space -> screen space matrix \n"
"uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0) \n"
"uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height) \n"
"uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit \n"
"uniform vec3 spotColor; // spotlight RGB color \n"
"// Custom vertex attributes\n"
"attribute vec4\tsubTexture;\t\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n"
"attribute vec4\ttexParams;\t\t// .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n"
"attribute float\ttexFormat;\t\t// T1RGB5 contour texture (if > 0)\n"
"attribute float\ttransLevel;\t\t// translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value\n"
"attribute float\tlightEnable;\t// lighting enabled (1.0) or luminous (0.0), drawn at full intensity\n"
"attribute float\tshininess;\t\t// specular shininess (if >= 0.0) or disable specular lighting (negative)\n"
"attribute float\tfogIntensity;\t// fog intensity (1.0, full fog effect, 0.0, no fog) \n"
"\n"
"// Custom vertex attributes \n"
"attribute vec4 subTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels) \n"
"attribute vec4 texParams; // .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode \n"
"attribute float texFormat; // .x=T1RGB5 contour texture (if > 0) \n"
"attribute float transLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value \n"
"attribute float lightEnable; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity \n"
"attribute float fogIntensity; // fog intensity (1.0, full fog effect, 0.0, no fog) \n"
"// Custom outputs to fragment shader\n"
"varying vec4\tfsSubTexture;\n"
"varying vec4\tfsTexParams;\n"
"varying float\tfsTexFormat;\n"
"varying float\tfsTransLevel;\n"
"varying vec3\tfsLightIntensity;\t// total light intensity for this vertex\n"
"varying float\tfsSpecularTerm;\t\t// specular light term (additive)\n"
"varying float\tfsFogFactor;\t\t// fog factor\n"
"varying float\tfsViewZ;\n"
"\n"
"// Custom outputs to fragment shader \n"
"varying vec4 fsSubTexture; \n"
"varying vec4 fsTexParams; \n"
"varying float fsTexFormat; \n"
"varying float fsTransLevel; \n"
"varying vec3 fsLightIntensity; // total light intensity for this vertex \n"
"varying float fsFogFactor; // fog factor \n"
"varying float fsViewZ; \n"
"// Gets the 3x3 matrix out of a 4x4 (because mat3(mat4matrix) does not work on ATI!)\n"
"mat3 GetLinearPart( mat4 m )\n"
"{\n"
"\tmat3 result;\n"
"\t\n"
"\tresult[0][0] = m[0][0]; \n"
"\tresult[0][1] = m[0][1]; \n"
"\tresult[0][2] = m[0][2]; \n"
"\n"
"// Gets the 3x3 matrix out of a 4x4 (because mat3(mat4matrix) does not work on ATI!) \n"
"mat3 GetLinearPart( mat4 m ) \n"
"{ \n"
" mat3 result; \n"
"\tresult[1][0] = m[1][0]; \n"
"\tresult[1][1] = m[1][1]; \n"
"\tresult[1][2] = m[1][2]; \n"
"\t\n"
"\tresult[2][0] = m[2][0]; \n"
"\tresult[2][1] = m[2][1]; \n"
"\tresult[2][2] = m[2][2]; \n"
"\t\n"
"\treturn result;\n"
"}\n"
"\n"
" result[0][0] = m[0][0]; \n"
" result[0][1] = m[0][1]; \n"
" result[0][2] = m[0][2]; \n"
"void main(void)\n"
"{\n"
"\tvec3\tviewVertex;\t\t// vertex coordinates in view space\n"
"\tvec3\tviewNormal;\t\t// vertex normal in view space\n"
"\tvec3\tsunVector;\t\t// sun lighting vector (as reflecting away from vertex)\n"
"\tfloat\tsunFactor;\t\t// sun light projection along vertex normal (0.0 to 1.0)\n"
"\tvec3\thalfway;\n"
"\tfloat\tspecFactor;\n"
"\t\n"
"\t// Transform vertex\n"
"\tgl_Position = projectionMatrix * modelViewMatrix * gl_Vertex;\n"
"\tviewVertex = vec3(modelViewMatrix * gl_Vertex);\t\n"
"\t\n"
"\t/*\n"
"\t * Modulation\n"
"\t *\n"
" \t * Polygon color serves as material color (modulating the light intensity)\n"
"\t * for textured polygons. The fragment shader will ignore (overwrite) the\n"
"\t * the color passed to it if the fragment is textured. \n"
"\t *\n"
"\t * Untextured fragments must be set to the polygon color and the light\n"
"\t * intensity is initialized to 1.0 here. Alpha must be set to 1.0 because\n"
"\t * the fragment shader multiplies it by the polygon translucency setting. \n"
"\t *\n"
"\t * TO-DO: Does OpenGL set alpha to 1.0 by default if no alpha is specified\n"
"\t * for the vertex? If so, we can remove that line from here.\n"
"\t */\n"
"\n"
" result[1][0] = m[1][0]; \n"
" result[1][1] = m[1][1]; \n"
" result[1][2] = m[1][2]; \n"
"\tgl_FrontColor = gl_Color;\t// untextured polygons will use this\n"
"\tgl_FrontColor.a = 1.0;\t\n"
"\tfsLightIntensity = vec3(1.0,1.0,1.0);\n"
"\tif (texParams.x > 0.5)\t\t// textured\n"
"\t\tfsLightIntensity *= gl_Color.rgb;\n"
"\t\t\n"
"\t/*\n"
" \t * Sun Light\n"
"\t *\n"
"\t * Parallel light source and ambient lighting are only applied for non-\n"
"\t * luminous polygons.\n"
" \t */\n"
"\tfsSpecularTerm = 0.0;\n"
" \tif (lightEnable > 0.5)\t// not luminous\n"
"\t{\n"
"\t\t// Normal -> view space\n"
"\t\tviewNormal = normalize(GetLinearPart(modelViewMatrix)*gl_Normal);\n"
"\n"
" result[2][0] = m[2][0]; \n"
" result[2][1] = m[2][1]; \n"
" result[2][2] = m[2][2]; \n"
"\t\t// Real3D -> OpenGL view space convention (TO-DO: do this outside of shader)\n"
"\t\tsunVector = lighting[0]*vec3(1.0,-1.0,-1.0);\n"
"\t\t\n"
"\t\t// Compute diffuse factor for sunlight\n"
"\t\tsunFactor = max(dot(sunVector,viewNormal),0.0);\n"
"\t\t\n"
"\t\t// Total light intensity: sum of all components\n"
"\t\tfsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y);\n"
"\t\t\n"
"\t\t/*\n"
"\t\t * Specular Lighting\n"
"\t\t *\n"
"\t\t * The specular term is treated similarly to the \"separate specular\n"
"\t\t * color\" functionality of OpenGL: it is added as a highlight in the\n"
"\t\t * fragment shader. This allows even black textures to be lit.\n"
"\t\t *\n"
"\t\t * TO-DO: Ambient intensity viewport parameter is known but what about\n"
"\t\t * the intensity of the specular term? Always applied with full \n"
"\t\t * intensity here but this is unlikely to be correct.\n"
"\t\t */\n"
" \t\tif (shininess >= 0.0)\n"
" \t\t{\n"
" \t\t\t// Standard specular lighting equation\n"
" \t\t\tvec3 V = normalize(-viewVertex);\n"
" \t\t\tvec3 H = normalize(sunVector+V);\t// halfway vector\n"
" \t\t\tfloat s = max(10,64-shininess);\t\t// seems to look nice, but probably not correct\n"
" \t\t\tfsSpecularTerm = pow(max(dot(viewNormal,H),0),s);\n"
" \t\t\tif (sunFactor <= 0) fsSpecularTerm = 0;\n"
" \t\t\t\n"
" \t\t\t// Faster approximation \t\t\t\n"
" \t\t\t//float temp = max(dot(viewNormal,H),0);\n"
" \t\t\t//float s = 64-shininess;\n"
" \t\t\t//fsSpecularTerm = temp/(s-temp*s+temp);\n"
" \t\t\t\n"
" \t\t\t// Phong formula\n"
" \t\t\t//vec3 R = normalize(2*dot(sunVector,viewNormal)*viewNormal - sunVector);\n"
" \t\t\t//vec3 V = normalize(-viewVertex);\n"
" \t\t\t//float s = max(2,64-shininess);\n"
" \t\t\t//fsSpecularTerm = pow(max(dot(R,V),0),s);\n"
" \t\t}\n"
"\t}\n"
"\t\n"
"\t// Fog\n"
"\tfloat z = length(viewVertex);\n"
"\tfsFogFactor = clamp(1.0-fogIntensity*(gl_Fog.start+z*gl_Fog.density), 0.0, 1.0);\n"
"\n"
" return result; \n"
"} \n"
"\t// Pass viewspace Z coordinate (for spotlight)\n"
"\tfsViewZ = -viewVertex.z;\t// convert Z from GL->Real3D convention (want +Z to be further into screen)\n"
"\n"
"void main(void) \n"
"{ \n"
" vec3 viewVertex; // vertex coordinates in view space \n"
" vec3 viewNormal; // vertex normal in view space \n"
" vec3 sunVector; // sun lighting vector (as reflecting away from vertex) \n"
" float sunFactor; // sun light projection along vertex normal (0.0 to 1.0) \n"
" vec3 halfway; \n"
" float specFactor; \n"
" \n"
" // Transform vertex \n"
" gl_Position = projectionMatrix * modelViewMatrix * gl_Vertex; \n"
" viewVertex = vec3(modelViewMatrix * gl_Vertex); \n"
" \n"
" /* \n"
" * Modulation \n"
" * \n"
" * Polygon color serves as material color (modulating the light intensity) \n"
" * for textured polygons. The fragment shader will ignore (overwrite) the \n"
" * the color passed to it if the fragment is textured. \n"
" * \n"
" * Untextured fragments must be set to the polygon color and the light \n"
" * intensity is initialized to 1.0 here. Alpha must be set to 1.0 because \n"
" * the fragment shader multiplies it by the polygon translucency setting. \n"
" * \n"
" * To-do: Does OpenGL set alpha to 1.0 by default if no alpha is specified \n"
" * for the vertex? If so, we can remove that line from here. \n"
" */ \n"
" \n"
" gl_FrontColor = gl_Color; // untextured polygons will use this \n"
" gl_FrontColor.a = 1.0; \n"
" fsLightIntensity = vec3(1.0,1.0,1.0); \n"
" if (texParams.x > 0.0) // textured \n"
" fsLightIntensity *= gl_Color.rgb; \n"
" \n"
" /* \n"
" * Sun Light \n"
" * \n"
" * Parallel light source and ambient lighting are only applied for non- \n"
" * luminous polygons. \n"
" */ \n"
" if (lightEnable > 0.5) // not luminous \n"
" { \n"
" // Normal -> view space \n"
" viewNormal = normalize(GetLinearPart(modelViewMatrix)*gl_Normal); \n"
" \n"
" // Real3D -> OpenGL view space convention (TO-DO: do this outside of shader) \n"
" sunVector = lighting[0]*vec3(1.0,-1.0,-1.0); \n"
" \n"
" // Compute diffuse factor for sunlight \n"
" sunFactor = max(dot(sunVector,viewNormal),0.0); \n"
" \n"
" // Total light intensity: sum of all components \n"
" fsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y); \n"
" } \n"
" \n"
" // Fog \n"
" float z = length(viewVertex); \n"
" fsFogFactor = clamp(1.0-fogIntensity*(gl_Fog.start+z*gl_Fog.density), 0.0, 1.0); \n"
" \n"
" // Pass viewspace Z coordinate (for spotlight) \n"
" fsViewZ = -viewVertex.z; // convert Z from GL->Real3D convention (want +Z to be further into screen) \n"
" \n"
" // Pass remaining parameters to fragment shader \n"
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
" fsSubTexture = subTexture; \n"
" fsTexParams = texParams; \n"
" fsTransLevel = transLevel; \n"
" fsTexFormat = texFormat; \n"
"\t// Pass remaining parameters to fragment shader\n"
"\tgl_TexCoord[0] = gl_MultiTexCoord0;\n"
"\tfsSubTexture = subTexture;\n"
"\tfsTexParams = texParams;\n"
"\tfsTransLevel = transLevel;\n"
"\tfsTexFormat = texFormat;\n"
"}\n"
};
@ -180,194 +215,198 @@ static const char vertexShaderSource[] =
// Fragment shader
static const char fragmentShaderSource[] =
{
"/** \n"
" ** Supermodel \n"
" ** A Sega Model 3 Arcade Emulator. \n"
" ** Copyright 2011 Bart Trzynadlowski \n"
" ** \n"
" ** This file is part of Supermodel. \n"
" ** \n"
" ** Supermodel is free software: you can redistribute it and/or modify it under \n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option) \n"
" ** any later version. \n"
" ** \n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT \n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for \n"
" ** more details. \n"
" ** \n"
" ** You should have received a copy of the GNU General Public License along \n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>. \n"
" **/ \n"
" \n"
"/* \n"
" * Fragment.glsl \n"
" * \n"
" * Fragment shader for 3D rendering. \n"
" */ \n"
" \n"
"#version 120 \n"
" \n"
"// Global uniforms \n"
"uniform sampler2D textureMap; // complete texture map, 2048x2048 texels \n"
"uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height) \n"
"uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit \n"
"uniform vec3 spotColor; // spotlight RGB color \n"
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011 Bart Trzynadlowski, Nik Henson \n"
" **\n"
" ** This file is part of Supermodel.\n"
" **\n"
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
" ** any later version.\n"
" **\n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
" ** more details.\n"
" **\n"
" ** You should have received a copy of the GNU General Public License along\n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
" **/\n"
" \n"
"/*\n"
" * Fragment.glsl\n"
" *\n"
" * Fragment shader for 3D rendering.\n"
" */\n"
"\n"
"// Inputs from vertex shader \n"
"varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels) \n"
"varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode \n"
"varying float fsTexFormat; // .x=T1RGB5 contour texture (if > 0) \n"
"varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque) \n"
"varying vec3 fsLightIntensity; // lighting intensity \n"
"varying float fsFogFactor; // fog factor \n"
"varying float fsViewZ; // Z distance to fragment from viewpoint at origin \n"
"#version 120\n"
"\n"
"/* \n"
" * WrapTexelCoords(): \n"
" * \n"
" * Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture \n"
" * sheet, taking into account wrapping behavior. \n"
" * \n"
" * Computing normalized OpenGL texture coordinates (0 to 1) within the \n"
" * Real3D texture sheet: \n"
" * \n"
" * If the texture is not mirrored, we simply have to clamp the \n"
" * coordinates to fit within the texture dimensions, add the texture \n"
" * X, Y position to select the appropriate one, and normalize by 2048 \n"
" * (the dimensions of the Real3D texture sheet). \n"
" * \n"
" * = [(u,v)%(w,h)+(x,y)]/(2048,2048) \n"
" * \n"
" * If mirroring is enabled, textures are mirrored every odd multiple of \n"
" * the original texture. To detect whether we are in an odd multiple, \n"
" * simply divide the coordinate by the texture dimension and check \n"
" * whether the result is odd. Then, clamp the coordinates as before but \n"
" * subtract from the last texel to mirror them: \n"
" * \n"
" * = [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048) \n"
" * where M is 1.0 if the texture must be mirrored. \n"
" * \n"
" * As an optimization, this function computes TWO texture coordinates \n"
" * simultaneously. The first is texCoord.xy, the second is in .zw. The other \n"
" * parameters must have .xy = .zw. \n"
" */ \n"
"vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable) \n"
"{ \n"
" vec4 clampedCoord, mirror, glTexCoord; \n"
" \n"
" clampedCoord = mod(texCoord,texSize); // clamp coordinates to within texture size \n"
" mirror = mirrorEnable * mod(floor(texCoord/texSize),2.0); // whether this texel needs to be mirrored \n"
" \n"
" glTexCoord = ( mirror*(texSize-clampedCoord) + \n"
" (vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord + \n"
" texOffset \n"
" ) / 2048.0; \n"
" return glTexCoord; \n"
"} \n"
"// Global uniforms\n"
"uniform sampler2D\ttextureMap;\t\t// complete texture map, 2048x2048 texels\n"
"uniform vec4\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
"uniform vec2\tspotRange;\t\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
"uniform vec3\tspotColor;\t\t\t// spotlight RGB color\n"
"uniform vec3\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
"\n"
"/* \n"
" * main(): \n"
" * \n"
" * Fragment shader entry point. \n"
" */ \n"
"// Inputs from vertex shader \n"
"varying vec4\t\tfsSubTexture;\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n"
"varying vec4\t\tfsTexParams;\t// .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n"
"varying float\t\tfsTexFormat;\t// T1RGB5 contour texture (if > 0)\n"
"varying float\t\tfsTransLevel;\t// translucence level, 0.0 (transparent) to 1.0 (opaque)\n"
"varying vec3\t\tfsLightIntensity;\t// lighting intensity \n"
"varying float\t\tfsSpecularTerm;\t// specular highlight\n"
"varying float\t\tfsFogFactor;\t// fog factor\n"
"varying float\t\tfsViewZ;\t\t// Z distance to fragment from viewpoint at origin\n"
"\n"
"void main(void) \n"
"{ \n"
" vec4 uv_top, uv_bot, c[4]; \n"
" vec2 r; \n"
" vec4 fragColor; \n"
" vec2 ellipse; \n"
" vec3 lightIntensity; \n"
" float insideSpot; \n"
" \n"
" // Get polygon color for untextured polygons (textured polygons will overwrite) \n"
" if (fsTexParams.x==0.0) \n"
" fragColor = gl_Color; \n"
" else \n"
" // Textured polygons: set fragment color to texel value \n"
" { \n"
" /* \n"
" * Bilinear Filtering \n"
" * \n"
" * In order to get this working on ATI, the number of operations is \n"
" * reduced by putting everything into vec4s. uv_top holds the UV \n"
" * coordinates for the top two texels (.xy=left, .zw=right) and uv_bot \n"
" * is for the lower two. \n"
" */ \n"
" \n"
" // Compute fractional blending factor, r, and lower left corner of texel 0 \n"
" uv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5); // move into the lower left blending texel \n"
" r = uv_bot.xy-floor(uv_bot.xy); // fractional part \n"
" uv_bot.xy = floor(uv_bot.xy); // integral part \n"
" \n"
" // Compute texel coordinates \n"
" uv_bot.xy += vec2(0.5,0.5); // offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia) \n"
" uv_bot.zw = uv_bot.xy + vec2(1.0,0.0); // compute coordinates of the other three neighbors \n"
" uv_top = uv_bot + vec4(0.0,1.0,0.0,1.0); \n"
" \n"
" // Compute the properly wrapped texel coordinates \n"
" uv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw)); \n"
" uv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw)); \n"
" \n"
" // Fetch the texels \n"
" c[0]=texture2D(textureMap,uv_bot.xy); // bottom-left (base texel) \n"
" c[1]=texture2D(textureMap,uv_bot.zw); // bottom-right \n"
" c[2]=texture2D(textureMap,uv_top.xy); // top-left \n"
" c[3]=texture2D(textureMap,uv_top.zw); // top-right \n"
" \n"
" // Interpolate texels and blend result with material color to determine final (unlit) fragment color \n"
" // fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t); \n"
" // Faster method: \n"
" c[0] += (c[1]-c[0])*r.s; // 2 alu \n"
" c[2] += (c[3]-c[2])*r.s; // 2 alu \n"
" fragColor = c[0]+(c[2]-c[0])*r.t; // 2 alu \n"
" \n"
" /* \n"
" * T1RGB5: \n"
" * \n"
" * The transparency bit determines whether to discard pixels (if set). \n"
" * What is unknown is how this bit behaves when interpolated. OpenGL \n"
" * processes it as an alpha value, so it might concievably be blended \n"
" * with neighbors. Here, an arbitrary threshold is chosen. \n"
" * \n"
" * To-do: blending could probably enabled and this would work even \n"
" * better with a hard threshold. \n"
" * \n"
" * Countour processing also seems to be enabled for RGBA4 textures. \n"
" * When the alpha value is 0.0 (or close), pixels are discarded \n"
" * entirely. \n"
" */ \n"
" if (fsTexParams.y > 0.0) // contour processing enabled \n"
" { \n"
" if (fragColor.a < 0.01) // discard anything with alpha == 0 \n"
" discard; \n"
" } \n"
" \n"
" // If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency \n"
" if (fsTexFormat > 0.0) // contour (T1RGB5) texture map \n"
" fragColor.a = 1.0; \n"
" } \n"
" \n"
" // Compute spotlight and apply lighting \n"
" ellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw; \n"
" insideSpot = dot(ellipse,ellipse); \n"
" if ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y)) \n"
" lightIntensity = fsLightIntensity+(1.0-insideSpot)*spotColor; \n"
" else \n"
" lightIntensity = fsLightIntensity; \n"
" fragColor.rgb *= lightIntensity; \n"
" \n"
" // Translucency (modulates existing alpha channel for RGBA4 texels) \n"
" fragColor.a *= fsTransLevel; \n"
" \n"
" // Apply fog under the control of fog factor setting from polygon header \n"
" fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor ); \n"
" \n"
" // Store final color \n"
" gl_FragColor = fragColor; \n"
"}\n"
"/*\n"
" * WrapTexelCoords():\n"
" *\n"
" * Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture\n"
" * sheet, taking into account wrapping behavior.\n"
" *\n"
" * Computing normalized OpenGL texture coordinates (0 to 1) within the \n"
" * Real3D texture sheet:\n"
" *\n"
" * If the texture is not mirrored, we simply have to clamp the\n"
" * coordinates to fit within the texture dimensions, add the texture\n"
" * X, Y position to select the appropriate one, and normalize by 2048\n"
" * (the dimensions of the Real3D texture sheet).\n"
" *\n"
" *\t\t= [(u,v)%(w,h)+(x,y)]/(2048,2048)\n"
" *\n"
" * If mirroring is enabled, textures are mirrored every odd multiple of\n"
" * the original texture. To detect whether we are in an odd multiple, \n"
" * simply divide the coordinate by the texture dimension and check \n"
" * whether the result is odd. Then, clamp the coordinates as before but\n"
" * subtract from the last texel to mirror them:\n"
" *\n"
" * \t\t= [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)\n"
" *\t\twhere M is 1.0 if the texture must be mirrored.\n"
" *\n"
" * As an optimization, this function computes TWO texture coordinates\n"
" * simultaneously. The first is texCoord.xy, the second is in .zw. The other\n"
" * parameters must have .xy = .zw.\n"
" */\n"
"vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)\n"
"{\n"
"\tvec4\tclampedCoord, mirror, glTexCoord;\n"
"\t\n"
"\tclampedCoord = mod(texCoord,texSize);\t\t\t\t\t\t// clamp coordinates to within texture size\n"
"\tmirror = mirrorEnable * mod(floor(texCoord/texSize),2.0);\t// whether this texel needs to be mirrored\n"
"\n"
"\tglTexCoord = (\tmirror*(texSize-clampedCoord) +\n"
"\t\t\t\t\t(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +\n"
"\t\t\t\t\ttexOffset\n"
"\t\t\t\t ) / 2048.0;\n"
"\treturn glTexCoord;\n"
"}\n"
"\n"
"/*\n"
" * main():\n"
" *\n"
" * Fragment shader entry point.\n"
" */\n"
"\n"
"void main(void)\n"
"{\t\n"
"\tvec4\tuv_top, uv_bot, c[4];\n"
"\tvec2\tr;\n"
"\tvec4\tfragColor;\n"
"\tvec2\tellipse;\n"
"\tvec3\tlightIntensity;\n"
"\tfloat\tinsideSpot;\n"
"\tint\t\tx;\n"
"\t\n"
"\t// Get polygon color for untextured polygons (textured polygons will overwrite)\n"
"\tif (fsTexParams.x < 0.5)\n"
"\t\tfragColor = gl_Color;\t\t\n"
"\telse\n"
"\t// Textured polygons: set fragment color to texel value\n"
"\t{\t\t\t\n"
"\t\t/*\n"
"\t\t * Bilinear Filtering\n"
"\t\t *\n"
"\t\t * In order to get this working on ATI, the number of operations is\n"
"\t\t * reduced by putting everything into vec4s. uv_top holds the UV \n"
"\t\t * coordinates for the top two texels (.xy=left, .zw=right) and uv_bot\n"
"\t\t * is for the lower two.\n"
"\t\t */\n"
"\n"
"\t\t// Compute fractional blending factor, r, and lower left corner of texel 0\n"
"\t\tuv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5);\t// move into the lower left blending texel \n"
"\t\tr = uv_bot.xy-floor(uv_bot.xy);\t\t\t\t\t// fractional part\n"
"\t\tuv_bot.xy = floor(uv_bot.xy);\t\t\t\t\t// integral part\n"
"\t\t\n"
"\t\t// Compute texel coordinates\n"
"\t\tuv_bot.xy += vec2(0.5,0.5);\t// offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia)\n"
"\t\tuv_bot.zw = uv_bot.xy + vec2(1.0,0.0);\t\t\t// compute coordinates of the other three neighbors\n"
"\t\tuv_top = uv_bot + vec4(0.0,1.0,0.0,1.0);\n"
"\n"
"\t\t// Compute the properly wrapped texel coordinates\n"
"\t\tuv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n"
"\t\tuv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n"
"\n"
"\t\t// Fetch the texels\n"
"\t\tc[0]=texture2D(textureMap,uv_bot.xy);\t// bottom-left (base texel)\n"
"\t\tc[1]=texture2D(textureMap,uv_bot.zw);\t// bottom-right\n"
"\t\tc[2]=texture2D(textureMap,uv_top.xy);\t// top-left\n"
"\t\tc[3]=texture2D(textureMap,uv_top.zw);\t// top-right\t\t\n"
"\n"
"\t\t// Interpolate texels and blend result with material color to determine final (unlit) fragment color\n"
"\t\t// fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t);\n"
"\t\t// Faster method:\n"
"\t\tc[0] += (c[1]-c[0])*r.s;\t\t\t// 2 alu\n"
"\t\tc[2] += (c[3]-c[2])*r.s;\t\t\t// 2 alu\n"
"\t\tfragColor = c[0]+(c[2]-c[0])*r.t;\t//2 alu\n"
"\t\n"
"\t\t/*\n"
"\t\t * T1RGB5:\n"
"\t\t *\n"
"\t\t * The transparency bit determines whether to discard pixels (if set).\n"
"\t\t * What is unknown is how this bit behaves when interpolated. OpenGL\n"
"\t\t * processes it as an alpha value, so it might concievably be blended\n"
"\t\t * with neighbors. Here, an arbitrary threshold is chosen.\n"
"\t\t *\n"
"\t\t * To-do: blending could probably enabled and this would work even\n"
"\t\t * better with a hard threshold.\n"
"\t\t *\n"
"\t\t * Countour processing also seems to be enabled for RGBA4 textures.\n"
"\t\t * When the alpha value is 0.0 (or close), pixels are discarded \n"
"\t\t * entirely.\n"
"\t\t */\n"
"\t\tif (fsTexParams.y > 0.5)\t// contour processing enabled\n"
"\t\t{\n"
"\t\t\tif (fragColor.a < 0.01)\t// discard anything with alpha == 0\n"
"\t\t\t\tdiscard;\n"
"\t\t}\n"
"\t\t\n"
"\t\t// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency\n"
"\t\tif (fsTexFormat > 0.0)\t\t// contour (T1RGB5) texture map\n"
"\t\t\tfragColor.a = 1.0;\n"
"\t}\n"
"\n"
"\t// Compute spotlight and apply lighting\n"
"\tellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;\n"
"\tinsideSpot = dot(ellipse,ellipse);\n"
"\tif ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y))\n"
"\t\tlightIntensity = fsLightIntensity+(1.0-insideSpot)*spotColor;\n"
"\telse\n"
"\t\tlightIntensity = fsLightIntensity;\n"
"\tfragColor.rgb *= lightIntensity;\n"
"\tfragColor.rgb += vec3(fsSpecularTerm,fsSpecularTerm,fsSpecularTerm);\n"
"\t\n"
"\t// Translucency (modulates existing alpha channel for RGBA4 texels)\n"
"\tfragColor.a *= fsTransLevel;\n"
"\n"
"\t// Apply fog under the control of fog factor setting from polygon header\n"
"\tfragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor);\n"
"\n"
"\t// Store final color\n"
"\tgl_FragColor = fragColor;\n"
"}\n"
};
#endif // INCLUDED_SHADERS3D_H