Supermodel/Src/Graphics/Shaders3D.h
Nik Henson 183dca563d Committing various small updates that have been hanging around in my source tree for a while now:
- Added 'crosshairs' command line and config option.
- Added 'vsync' command line and config option (so far only tested on NVidia cards on Windows 7 - other graphics drivers, O/Ss or driver settings may simply chose to ignore this).
- Added fullscreen toggle within game using Alt+Enter key combination.
- Added framework for lamp outputs and 'outputs' command line and config option.  So far only the lamps for driving games are hooked up in the emulator (others to be added later).
- Added an initial outputs implementation for Windows that sends MAMEHooker compatible messages (-outputs=win to enable)
- Fixed fps calculation in Main.cpp that was producing incorrect results and so giving the impression that frame throttling wasn't working properly when in fact it was.
- Fixed palette indexed colours as the index was always off by one, causing incorrect colours in various games, eg drivers' suits and flashing Start sign in Daytona 2.
- Altered caching of models so that models with palette indexed colours use the dynamic cache rather than the static one.  This is so that changes in palette indexed colours appear on screen, eg the flashing Start sign on the advanced course of Daytona 2 (although currently the START message itself is not visible due to other problems with texture decoding).
- Fixed small bug in TileGen.cpp which meant both palettes were being completely recomputed pretty much with every frame.  This was a significant performance hit, particularly as palette recomputation is currently being done in SyncSnapshots (it should be moved out of here at some point, although for now it's no big deal).
- Made sure all OpenGL objects and resources are deleted in Render2D/3D destructors, in particular the deleting of the VBO buffer in DestroyModelCache.
- Made sure that GLSL uniforms are always checked to see if they are bound before using them in order to stop unecessary (but harmless) GL errors.
- Altered the default texture sheet handling to use a single large GL texture holding multiple Model3 texture sheets rather than multiple GL textures as before (if required, the old behaviour can still be selected with the mulisheet fragment shader).  I believe this fixes the disappearing crosshairs/corrupt GL state problem which the multisheet fragment shader seemed to be triggering somehow.
- Fixed a bug in debugger which meant memory watches were not triggering properly
2012-07-15 21:04:46 +00:00

664 lines
30 KiB
C

/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Shaders3D.h
*
* Header file containing the 3D vertex and fragment shaders.
*/
#ifndef INCLUDED_SHADERS3D_H
#define INCLUDED_SHADERS3D_H
// Vertex shader
static const char vertexShaderSource[] =
{
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011-2012 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"
"// 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"
"// 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\ttexMap;\t\t// texture map number\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 outputs to fragment shader\n"
"varying vec4\tfsSubTexture;\n"
"varying vec4\tfsTexParams;\n"
"varying float\tfsTexFormat;\n"
"varying float\tfsTexMap;\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"
"// 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"
"\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"
"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"
"\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"
"\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.0,64.0-shininess);\t\t// seems to look nice, but probably not correct\n"
" \t\t\tfsSpecularTerm = pow(max(dot(viewNormal,H),0.0),s);\n"
" \t\t\tif (sunFactor <= 0.0) fsSpecularTerm = 0.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.0);\n"
" \t\t\t//float s = 64.0-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.0*dot(sunVector,viewNormal)*viewNormal - sunVector);\n"
" \t\t\t//vec3 V = normalize(-viewVertex);\n"
" \t\t\t//float s = max(2.0,64.0-shininess);\n"
" \t\t\t//fsSpecularTerm = pow(max(dot(R,V),0.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"
"\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"
"\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"
"\tfsTexMap = texMap;\n"
"}\n"
};
// Fragment shader (single texture sheet)
static const char fragmentShaderSingleSheetSource[] =
{
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011-2012 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"
"#version 120\n"
"\n"
"// Global uniforms\n"
"uniform sampler2D\ttextureMap;\t\t// complete texture map, 2048x2048 texels\n"
"uniform vec4\t\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
"uniform vec2\t\tspotRange;\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
"uniform vec3\t\tspotColor;\t\t// spotlight RGB color\n"
"uniform vec3\t\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
"uniform float\t\tmapSize;\t\t// texture map size (2048,4096,6144 etc)\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\tfsTexMap;\t\t// texture map number\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"
"/*\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 ) / mapSize;\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.5)\t\t// contour (T1RGB5) texture\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"
};
// Fragment shader (8 texture sheets)
static const char fragmentShaderMultiSheetSource[] =
{
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011-2012 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_MultiSheet.glsl\n"
" *\n"
" * Fragment shader for 3D rendering. Uses 8 texture sheets to decode the \n"
" * different possible formats.\n"
" */\n"
"\n"
"#version 120\n"
"\n"
"// Global uniforms\n"
"uniform sampler2D\ttextureMap0;\t\t// complete texture map (fmt 0), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap1;\t\t// complete texture map (fmt 1), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap2;\t\t// complete texture map (fmt 2), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap3;\t\t// complete texture map (fmt 3), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap4;\t\t// complete texture map (fmt 4), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap5;\t\t// complete texture map (fmt 5), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap6;\t\t// complete texture map (fmt 6), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap7;\t\t// complete texture map (fmt 7), 2048x2048 texels\n"
"uniform vec4\t\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
"uniform vec2\t\tspotRange;\t\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
"uniform vec3\t\tspotColor;\t\t\t// spotlight RGB color\n"
"uniform vec3\t\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
"uniform float\t\tmapSize;\t\t// texture map size (2048,4096,6144 etc)\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\tfsTexMap;\t// texture map number\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"
"/*\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 ) / mapSize;\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 from the given texture map\n"
"\t\tif (fsTexMap < 0.5f)\t{\n"
"\t\t\tc[0]=texture2D(textureMap0, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap0, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap0, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap0, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 1.5f) {\n"
" c[0]=texture2D(textureMap1, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap1, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap1, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap1, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 2.5f) {\n"
" c[0]=texture2D(textureMap2, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap2, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap2, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap2, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 3.5f) {\n"
" c[0]=texture2D(textureMap3, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap3, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap3, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap3, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 4.5f) {\n"
" c[0]=texture2D(textureMap4, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap4, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap4, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap4, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 5.5f) {\n"
" c[0]=texture2D(textureMap5, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap5, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap5, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap5, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 6.5f) {\n"
"\t\t\tc[0]=texture2D(textureMap6, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap6, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap6, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap6, uv_top.zw); // top-right\n"
"\t\t} else {\n"
" c[0]=texture2D(textureMap7, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap7, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap7, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap7, uv_top.zw); // top-right\n"
"\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.5)\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