From 85583acc9cf18f50e802d64607606d3b72f410c2 Mon Sep 17 00:00:00 2001 From: myT Date: Fri, 21 Feb 2020 08:26:12 +0100 Subject: [PATCH] dynamic lights apply to even more surfaces and have a nicer fall-off - ditched vertex colors (not wanted) and alpha tests (not needed) in the shaders - using a Bezier fall-off to get much softer edges - added no-depth-write transparent surfaces support by adjusting the depth test - multiplying the diffuse texture's color by its alpha in non-opaque passes - fixed triangle rejection based on cull type and normal direction - reflecting normals in shaders to support two-sided surfaces - rejecting surfaces with no diffuse stage or bad blend states as early as possible - liquids get lit weaker than other surfaces --- changelog.txt | 12 ++--- code/renderer/hlsl/dl.hlsl | 32 +++++++------- code/renderer/tr_backend.cpp | 11 +++++ code/renderer/tr_backend_d3d11.cpp | 15 +++---- code/renderer/tr_backend_gl2.cpp | 28 ++++++++---- code/renderer/tr_backend_gl3.cpp | 71 +++++++++++++++++------------- code/renderer/tr_local.h | 9 ++++ code/renderer/tr_shade.cpp | 8 ++-- code/renderer/tr_world.cpp | 15 +++++++ 9 files changed, 128 insertions(+), 73 deletions(-) diff --git a/changelog.txt b/changelog.txt index 087be77..bf41e74 100644 --- a/changelog.txt +++ b/changelog.txt @@ -89,13 +89,15 @@ add: r_mapBrightness now works on q3map2's external lightmap atlas images add: the renderer can now batch surfaces with different (but sufficiently similar) shaders this new optimization works with the output of "Frozen Sand Particle Studio" +add: /modellist /skinlist /imagelist /shaderlist can now filter results with pattern matching + +chg: dynamic lights now use a softer fall-off curve to create softer edges + chg: reverted an old change to the Escape key handling for cgame this fixes demo playback behavior in CPMA, which enables custom binds after pressing Escape once chg: searching for valid sample counts for MSAA in GL2 and GL3 instead of failing right away -add: /modellist /skinlist /imagelist /shaderlist can now filter results with pattern matching - chg: switched away from stb_image for .tga file loading and now using an updated version of the old code this lowers the chances of a fatal error from the zone memory allocator when reading large images @@ -106,8 +108,8 @@ chg: removed FreeType 2 and the unused R_REGISTERFONT syscalls that were using i chg: removed light flares and all related cvars (r_flare*) chg: r_textureMode (default: "best") is now latched and only supports 2 modes: - r_textureMode GL_NEAREST = LEGO(R) mode - nearest-neighbor filtering for a subset of surfaces - r_textureMode anything else = normal mode - the engine picks the most appropriate filters + r_textureMode GL_NEAREST = LEGO(R) mode - nearest-neighbor filtering for a subset of surfaces + r_textureMode anything else = normal mode - the engine picks the most appropriate filters chg: r_measureOverdraw was removed @@ -140,7 +142,7 @@ fix: broken face normals get fixed up on map load, which helps with dynamic ligh fix: classes of transparent surfaces (sprites, beams, etc) now get drawn in the right order -fix: dynamic lights can now affect transparent surfaces +fix: dynamic lights can now affect transparent surfaces and double-sided surfaces on both sides fix: dynamic lights no longer "reveal" the diffuse texture when r_lightmap is 1 diff --git a/code/renderer/hlsl/dl.hlsl b/code/renderer/hlsl/dl.hlsl index 98b112d..96acfc4 100644 --- a/code/renderer/hlsl/dl.hlsl +++ b/code/renderer/hlsl/dl.hlsl @@ -33,7 +33,6 @@ struct VIn { float4 position : POSITION; float4 normal : NORMAL; - float4 color : COLOR0; float2 texCoords : TEXCOORD0; }; @@ -41,7 +40,6 @@ struct VOut { float4 position : SV_Position; float3 normal : NORMAL; - float4 color : COLOR0; float2 texCoords : TEXCOORD0; float3 osLightVec : TEXCOORD1; float3 osEyeVec : TEXCOORD2; @@ -55,7 +53,6 @@ VOut vs_main(VIn input) VOut output; output.position = mul(projectionMatrix, positionVS); output.normal = input.normal.xyz; - output.color = input.color; output.texCoords = input.texCoords; output.osLightVec = osLightPos.xyz - input.position.xyz; output.osEyeVec = osEyePos.xyz - input.position.xyz; @@ -68,36 +65,39 @@ cbuffer PixelShaderBuffer { float3 lightColor; float lightRadius; - uint alphaTest; - uint dummy[3]; + float opaque; + float intensity; + float dummy[2]; }; Texture2D texture0 : register(t0); SamplerState sampler0 : register(s0); +float BezierEase(float t) +{ + return t * t * (3.0 - 2.0 * t); +} + float4 ps_main(VOut input) : SV_TARGET { - float4 base = texture0.Sample(sampler0, input.texCoords) * input.color; - if((alphaTest == 1 && base.a == 0.0) || - (alphaTest == 2 && base.a >= 0.5) || - (alphaTest == 3 && base.a < 0.5)) - discard; - + float4 base = texture0.Sample(sampler0, input.texCoords); float3 nL = normalize(input.osLightVec); // normalized object-space light vector float3 nV = normalize(input.osEyeVec); // normalized object-space view vector float3 nN = input.normal; // normalized object-space normal vector // light intensity - float intensFactor = dot(input.osLightVec, input.osLightVec) * lightRadius; - float3 intens = lightColor * (1.0 - intensFactor); + float intensFactor = min(dot(input.osLightVec, input.osLightVec) * lightRadius, 1.0); + float3 intens = lightColor * BezierEase(1.0 - sqrt(intensFactor)); // specular reflection term (N.H) - float specFactor = clamp(dot(nN, normalize(nL + nV)), 0.0, 1.0); + float specFactor = min(abs(dot(nN, normalize(nL + nV))), 1.0); float spec = pow(specFactor, 16.0) * 0.25; // Lambertian diffuse reflection term (N.L) - float diffuse = clamp(dot(nN, nL), 0.0, 1.0); - float4 final = (base * float4(diffuse.rrr, 1.0) + float4(spec.rrr, 1.0)) * float4(intens.rgb, 1.0); + float diffuse = min(abs(dot(nN, nL)), 1.0); + float3 color = (base.rgb * diffuse.rrr + spec.rrr) * intens * intensity; + float alpha = lerp(opaque, 1.0, base.a); + float4 final = float4(color.rgb * alpha, alpha); return final; } diff --git a/code/renderer/tr_backend.cpp b/code/renderer/tr_backend.cpp index 4e24d5c..5123e19 100644 --- a/code/renderer/tr_backend.cpp +++ b/code/renderer/tr_backend.cpp @@ -390,8 +390,10 @@ static void RB_RenderLitSurfList( dlight_t* dl, qbool opaque ) double originalTime = backEnd.refdef.floatTime; // draw everything + const int liquidFlags = CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER; oldEntityNum = -1; backEnd.currentEntity = &tr.worldEntity; + backEnd.dlOpaque = opaque; oldDepthRange = qfalse; depthRange = qfalse; tess.light = dl; @@ -418,6 +420,15 @@ static void RB_RenderLitSurfList( dlight_t* dl, qbool opaque ) RB_EndSurface(); RB_BeginSurface( shader, fogNum ); + // stage index is guaranteed valid by R_AddLitSurface + const int stageIndex = tess.shader->lightingStages[ST_DIFFUSE]; + const shaderStage_t* const stage = tess.xstages[stageIndex]; + backEnd.dlIntensity = (shader->contentFlags & liquidFlags) != 0 ? 0.5f : 1.0f; + backEnd.dlStateBits = + (opaque || (stage->stateBits & GLS_ATEST_BITS) != 0) ? + (GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL): + (GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); + sort = litSurf->sort; // diff --git a/code/renderer/tr_backend_d3d11.cpp b/code/renderer/tr_backend_d3d11.cpp index bedd38e..45d0393 100644 --- a/code/renderer/tr_backend_d3d11.cpp +++ b/code/renderer/tr_backend_d3d11.cpp @@ -177,8 +177,9 @@ struct DynamicLightPSData { float lightColor[3]; float lightRadius; - uint32_t alphaTest; // AlphaTest enum - uint32_t dummy[3]; + float opaque; + float intensity; + float dummy[2]; }; struct PostVSData @@ -673,9 +674,10 @@ static void UploadPendingShaderData() memcpy(vsData.clipPlane, d3d.clipPlane, sizeof(vsData.clipPlane)); memcpy(vsData.osEyePos, d3d.osEyePos, sizeof(vsData.osEyePos)); memcpy(vsData.osLightPos, d3d.osLightPos, sizeof(vsData.osLightPos)); - psData.alphaTest = d3d.alphaTest; memcpy(psData.lightColor, d3d.lightColor, sizeof(psData.lightColor)); psData.lightRadius = d3d.lightRadius; + psData.opaque = backEnd.dlOpaque ? 1.0f : 0.0f; + psData.intensity = backEnd.dlIntensity; ResetShaderData(pipeline->vertexBuffer, &vsData, sizeof(vsData)); ResetShaderData(pipeline->pixelBuffer, &psData, sizeof(psData)); } @@ -2268,7 +2270,6 @@ static void DrawDynamicLight() AppendVertexData(&d3d.vertexBuffers[VB_POSITION], tess.xyz, tess.numVertexes); AppendVertexData(&d3d.vertexBuffers[VB_NORMAL], tess.normal, tess.numVertexes); AppendVertexData(&d3d.vertexBuffers[VB_TEXCOORD], tess.svars[stageIndex].texcoordsptr, tess.numVertexes); - AppendVertexData(&d3d.vertexBuffers[VB_COLOR], tess.svars[stageIndex].colors, tess.numVertexes); } else { @@ -2277,13 +2278,11 @@ static void DrawDynamicLight() pointers[VB_NORMAL] = tess.normal; pointers[VB_TEXCOORD] = tess.svars[stageIndex].texcoordsptr; pointers[VB_TEXCOORD2] = NULL; - pointers[VB_COLOR] = tess.svars[stageIndex].colors; + pointers[VB_COLOR] = NULL; AppendVertexDataGroup(pointers, tess.numVertexes); } - const int oldAlphaTestBits = stage->stateBits & GLS_ATEST_BITS; - const int newBits = GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; - ApplyState(oldAlphaTestBits | newBits, tess.shader->cullType, tess.shader->polygonOffset); + ApplyState(backEnd.dlStateBits, tess.shader->cullType, tess.shader->polygonOffset); BindBundle(0, &stage->bundle); UploadPendingShaderData(); diff --git a/code/renderer/tr_backend_gl2.cpp b/code/renderer/tr_backend_gl2.cpp index f8ac6ed..dbe2d5c 100644 --- a/code/renderer/tr_backend_gl2.cpp +++ b/code/renderer/tr_backend_gl2.cpp @@ -93,6 +93,7 @@ struct GLSL_DynLightProgramAttribs { // pixel shader: GLint texture; // 2D texture GLint lightColorRadius; // 4f, w = 1 / (r^2) + GLint opaqueIntensity; // 2f }; static GLSL_DynLightProgramAttribs dynLightProgAttribs; @@ -120,6 +121,8 @@ static void DrawDynamicLight() if ( tess.shader->polygonOffset ) glEnable( GL_POLYGON_OFFSET_FILL ); + glUniform2f( dynLightProgAttribs.opaqueIntensity, backEnd.dlOpaque ? 1.0f : 0.0f, backEnd.dlIntensity ); + const int stage = tess.shader->lightingStages[ST_DIFFUSE]; const shaderStage_t* pStage = tess.xstages[ stage ]; @@ -136,9 +139,7 @@ static void DrawDynamicLight() glVertexPointer( 3, GL_FLOAT, 16, tess.xyz ); glLockArraysEXT( 0, tess.numVertexes ); - const int oldAlphaTestBits = pStage->stateBits & GLS_ATEST_BITS; - const int newBits = GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; - GL_State( oldAlphaTestBits | newBits ); + GL_State( backEnd.dlStateBits ); GL_SelectTexture( 0 ); R_BindAnimatedImage( &pStage->bundle ); @@ -300,25 +301,33 @@ static const char* dynLightVS = static const char* dynLightFS = "uniform sampler2D texture;\n" -"uniform vec4 lightColorRadius;" // w = 1 / (r^2) +"uniform vec4 lightColorRadius;\n" // w = 1 / (r^2) +"uniform vec2 opaqueIntensity;\n" "varying vec4 L;\n" // object-space light vector "varying vec4 V;\n" // object-space view vector "varying vec3 nN;\n" // normalized object-space normal vector "\n" +"float BezierEase(float t)\n" +"{\n" +" return t * t * (3.0 - 2.0 * t);\n" +"}\n" +"\n" "void main()\n" "{\n" " vec4 base = texture2D(texture, gl_TexCoord[0].xy);\n" " vec3 nL = normalize(L.xyz);\n" // normalized light vector " vec3 nV = normalize(V.xyz);\n" // normalized view vector // light intensity -" float intensFactor = dot(L.xyz, L.xyz) * lightColorRadius.w;" -" vec3 intens = lightColorRadius.rgb * (1.0 - intensFactor);\n" +" float intensFactor = min(dot(L.xyz, L.xyz) * lightColorRadius.w, 1.0);" +" vec3 intens = lightColorRadius.rgb * BezierEase(1.0 - sqrt(intensFactor));\n" // specular reflection term (N.H) -" float specFactor = clamp(dot(nN, normalize(nL + nV)), 0.0, 1.0);\n" +" float specFactor = min(abs(dot(nN, normalize(nL + nV))), 1.0);\n" " float spec = pow(specFactor, 16.0) * 0.25;\n" // Lambertian diffuse reflection term (N.L) -" float diffuse = clamp(dot(nN, nL), 0.0, 1.0);\n" -" gl_FragColor = vec4((base.rgb * vec3(diffuse) + vec3(spec)) * vec3(intens), base.a);\n" +" float diffuse = min(abs(dot(nN, nL)), 1.0);\n" +" vec3 color = (base.rgb * vec3(diffuse) + vec3(spec)) * intens * opaqueIntensity.y;\n" +" float alpha = mix(opaqueIntensity.x, 1.0, base.a);\n" +" gl_FragColor = vec4(color.rgb * alpha, alpha);\n" "}\n" ""; @@ -696,6 +705,7 @@ static qbool GL2_Init() dynLightProgAttribs.osLightPos = glGetUniformLocation( dynLightProg.p, "osLightPos" ); dynLightProgAttribs.texture = glGetUniformLocation( dynLightProg.p, "texture" ); dynLightProgAttribs.lightColorRadius = glGetUniformLocation( dynLightProg.p, "lightColorRadius" ); + dynLightProgAttribs.opaqueIntensity = glGetUniformLocation( dynLightProg.p, "opaqueIntensity" ); if ( !GL2_CreateProgram( gammaProg, gammaVS, gammaFS ) ) { ri.Printf( PRINT_ERROR, "Failed to compile gamma correction shaders\n" ); diff --git a/code/renderer/tr_backend_gl3.cpp b/code/renderer/tr_backend_gl3.cpp index 561ed37..496ee2e 100644 --- a/code/renderer/tr_backend_gl3.cpp +++ b/code/renderer/tr_backend_gl3.cpp @@ -161,7 +161,8 @@ enum DynamicLightUniform DU_LIGHT_POS, DU_EYE_POS, DU_LIGHT_COLOR_RADIUS, - DU_ALPHA_TEST, + DU_OPAQUE, + DU_INTENSITY, DU_COUNT }; @@ -233,6 +234,8 @@ struct OpenGL3 qbool enableClipPlane; qbool prevEnableClipPlane; AlphaTest alphaTest; + qbool dlOpaque; + float dlIntensity; ArrayBuffer arrayBuffers[VB_COUNT]; ArrayBuffer indexBuffer; @@ -400,11 +403,9 @@ static const char* dl_vs = "in vec4 position;\n" "in vec4 normal;\n" "in vec2 texCoords1;\n" -"in vec4 color;\n" "\n" "out vec3 normalFS;\n" "out vec2 texCoords1FS;\n" -"out vec4 colorFS;\n" "out vec3 L;\n" "out vec3 V;\n" "\n" @@ -415,7 +416,6 @@ static const char* dl_vs = " gl_ClipDistance[0] = dot(positionVS, clipPlane);\n" " normalFS = normal.xyz;\n" " texCoords1FS = texCoords1;\n" -" colorFS = color;\n" " L = osLightPos - position.xyz;\n" " V = osEyePos - position.xyz;\n" "}\n"; @@ -424,40 +424,41 @@ static const char* dl_fs = "uniform sampler2D texture1;\n" "\n" "uniform vec4 lightColorRadius;\n" -"uniform uint alphaTest;\n" +"uniform float opaque;\n" +"uniform float intensity;\n" "\n" "in vec3 normalFS;\n" "in vec2 texCoords1FS;\n" -"in vec4 colorFS;\n" "in vec3 L;\n" "in vec3 V;\n" "\n" "out vec4 fragColor;\n" "\n" +"float BezierEase(float t)\n" +"{\n" +" return t * t * (3.0 - 2.0 * t);\n" +"}\n" +"\n" "void main()\n" "{\n" -" vec4 base = colorFS * texture2D(texture1, texCoords1FS);\n" +" vec4 base = texture2D(texture1, texCoords1FS);\n" " vec3 nL = normalize(L);\n" " vec3 nV = normalize(V);\n" "\n" " // light intensity\n" -" float intensFactor = dot(L, L) * lightColorRadius.w;\n" -" vec3 intens = lightColorRadius.rgb * (1.0 - intensFactor);\n" +" float intensFactor = min(dot(L, L) * lightColorRadius.w, 1.0);\n" +" vec3 intens = lightColorRadius.rgb * BezierEase(1.0 - sqrt(intensFactor));\n" "\n" " // specular reflection term (N.H)\n" -" float specFactor = clamp(dot(normalFS, normalize(nL + nV)), 0.0, 1.0);\n" +" float specFactor = min(abs(dot(normalFS, normalize(nL + nV))), 1.0);\n" " float spec = pow(specFactor, 16.0) * 0.25;\n" "\n" " // Lambertian diffuse reflection term (N.L)\n" -" float diffuse = clamp(dot(normalFS, nL), 0.0, 1.0);\n" -" vec4 r = vec4((base.rgb * vec3(diffuse) + vec3(spec)) * intens, base.a);\n" +" float diffuse = min(abs(dot(normalFS, nL)), 1.0);\n" +" vec3 color = (base.rgb * vec3(diffuse) + vec3(spec)) * intens * intensity;\n" +" float alpha = mix(opaque, 1.0, base.a);\n" "\n" -" if( (alphaTest == uint(1) && r.a == 0.0) ||\n" -" (alphaTest == uint(2) && r.a >= 0.5) ||\n" -" (alphaTest == uint(3) && r.a < 0.5))\n" -" discard;\n" -"\n" -" fragColor = r;\n" +" fragColor = vec4(color.rgb * alpha, alpha);\n" "}\n"; static const char* sprite_vs = @@ -1412,10 +1413,6 @@ static void ApplyAlphaTest(AlphaTest alphaTest) { gl.pipelines[PID_GENERIC].uniformsDirty[GU_ALPHA_TEX] = qtrue; } - else if(gl.pipelineId == PID_DYNAMIC_LIGHT) - { - gl.pipelines[PID_DYNAMIC_LIGHT].uniformsDirty[DU_ALPHA_TEST] = qtrue; - } else if(gl.pipelineId == PID_SOFT_SPRITE) { gl.pipelines[PID_SOFT_SPRITE].uniformsDirty[SU_ALPHA_TEST] = qtrue; @@ -1884,15 +1881,14 @@ static void Init() gl.pipelines[PID_DYNAMIC_LIGHT].arrayBuffers[VB_NORMAL].attribName = "normal"; gl.pipelines[PID_DYNAMIC_LIGHT].arrayBuffers[VB_TEXCOORD].enabled = qtrue; gl.pipelines[PID_DYNAMIC_LIGHT].arrayBuffers[VB_TEXCOORD].attribName = "texCoords1"; - gl.pipelines[PID_DYNAMIC_LIGHT].arrayBuffers[VB_COLOR].enabled = qtrue; - gl.pipelines[PID_DYNAMIC_LIGHT].arrayBuffers[VB_COLOR].attribName = "color"; gl.pipelines[PID_DYNAMIC_LIGHT].uniformNames[DU_MODELVIEW] = "modelView"; gl.pipelines[PID_DYNAMIC_LIGHT].uniformNames[DU_PROJECTION] = "projection"; gl.pipelines[PID_DYNAMIC_LIGHT].uniformNames[DU_CLIP_PLANE] = "clipPlane"; gl.pipelines[PID_DYNAMIC_LIGHT].uniformNames[DU_LIGHT_POS] = "osLightPos"; gl.pipelines[PID_DYNAMIC_LIGHT].uniformNames[DU_EYE_POS] = "osEyePos"; gl.pipelines[PID_DYNAMIC_LIGHT].uniformNames[DU_LIGHT_COLOR_RADIUS] = "lightColorRadius"; - gl.pipelines[PID_DYNAMIC_LIGHT].uniformNames[DU_ALPHA_TEST] = "alphaTest"; + gl.pipelines[PID_DYNAMIC_LIGHT].uniformNames[DU_OPAQUE] = "opaque"; + gl.pipelines[PID_DYNAMIC_LIGHT].uniformNames[DU_INTENSITY] = "intensity"; gl.pipelines[PID_SOFT_SPRITE].arrayBuffers[VB_POSITION].enabled = qtrue; gl.pipelines[PID_SOFT_SPRITE].arrayBuffers[VB_POSITION].attribName = "position"; @@ -2209,14 +2205,23 @@ static void DrawDynamicLight() UploadVertexArray(VB_POSITION, tess.xyz); UploadVertexArray(VB_NORMAL, tess.normal); UploadVertexArray(VB_TEXCOORD, tess.svars[stageIndex].texcoordsptr); - UploadVertexArray(VB_COLOR, tess.svars[stageIndex].colors); UploadIndices(tess.dlIndexes, tess.dlNumIndexes); - const int oldAlphaTestBits = stage->stateBits & GLS_ATEST_BITS; - const int newBits = GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; - ApplyState(oldAlphaTestBits | newBits, tess.shader->cullType, tess.shader->polygonOffset); + ApplyState(backEnd.dlStateBits, tess.shader->cullType, tess.shader->polygonOffset); BindBundle(0, &stage->bundle); + if(backEnd.dlOpaque != gl.dlOpaque) + { + gl.dlOpaque = backEnd.dlOpaque; + pipeline->uniformsDirty[DU_OPAQUE] = qtrue; + } + + if(backEnd.dlIntensity != gl.dlIntensity) + { + gl.dlIntensity = backEnd.dlIntensity; + pipeline->uniformsDirty[DU_INTENSITY] = qtrue; + } + if(pipeline->uniformsDirty[DU_MODELVIEW]) { glUniformMatrix4fv(pipeline->uniformLocations[DU_MODELVIEW], 1, GL_FALSE, gl.modelViewMatrix); @@ -2229,9 +2234,13 @@ static void DrawDynamicLight() { glUniform4fv(pipeline->uniformLocations[DU_CLIP_PLANE], 1, gl.clipPlane); } - if(pipeline->uniformsDirty[DU_ALPHA_TEST]) + if(pipeline->uniformsDirty[DU_OPAQUE]) { - glUniform1ui(pipeline->uniformLocations[DU_ALPHA_TEST], gl.alphaTest); + glUniform1f(pipeline->uniformLocations[DU_OPAQUE], gl.dlOpaque ? 1.0f : 0.0f); + } + if(pipeline->uniformsDirty[DU_INTENSITY]) + { + glUniform1f(pipeline->uniformLocations[DU_INTENSITY], gl.dlIntensity); } memset(pipeline->uniformsDirty, 0, sizeof(pipeline->uniformsDirty)); diff --git a/code/renderer/tr_local.h b/code/renderer/tr_local.h index bcc4526..b75dd81 100644 --- a/code/renderer/tr_local.h +++ b/code/renderer/tr_local.h @@ -815,6 +815,15 @@ typedef struct { byte color2D[4]; trRefEntity_t entity2D; // currentEntity will point at this when doing 2D rendering + // dynamic lights data set by the back-end for the GAL + qbool dlOpaque; // qtrue when drawing an opaque surface + float dlIntensity; // 1 for most surfaces, but can be scaled down for liquids etc. + unsigned int dlStateBits; // the state bits to apply for this draw call + // quick explanation on why dlOpaque is useful in the first place: + // - opaque surfaces can have a diffuse texture whose alpha isn't 255 everywhere + // - when that happens and we multiply the color by the the alpha (DL uses additive blending), + // we get "light holes" in opaque surfaces, which is not what we want + int* pc; // current stats set, depending on projection2D int pc2D[RB_STATS_MAX]; int pc3D[RB_STATS_MAX]; diff --git a/code/renderer/tr_shade.cpp b/code/renderer/tr_shade.cpp index 6b5051f..7f3a0ae 100644 --- a/code/renderer/tr_shade.cpp +++ b/code/renderer/tr_shade.cpp @@ -57,19 +57,19 @@ void RB_BeginSurface( const shader_t* shader, int fogNum ) static void RB_DrawDynamicLight() { - if (tess.shader->lightingStages[ST_DIFFUSE] == -1) - return; - backEnd.pc[RB_LIT_VERTICES_LATECULLTEST] += tess.numVertexes; static byte clipBits[SHADER_MAX_VERTEXES]; const dlight_t* dl = tess.light; + const cullType_t cullType = tess.shader->cullType; for (int i = 0; i < tess.numVertexes; ++i) { vec3_t dist; VectorSubtract(dl->transformed, tess.xyz[i], dist); - if (DotProduct(dist, tess.normal[i]) <= 0.0f) { + const float dp = DotProduct(dist, tess.normal[i]); + if (cullType == CT_FRONT_SIDED && dp <= 0.0f || + cullType == CT_BACK_SIDED && dp >= 0.0f) { clipBits[i] = byte(-1); continue; } diff --git a/code/renderer/tr_world.cpp b/code/renderer/tr_world.cpp index e4598b4..af61843 100644 --- a/code/renderer/tr_world.cpp +++ b/code/renderer/tr_world.cpp @@ -331,12 +331,27 @@ static void R_AddLitSurface( msurface_t* surf, const dlight_t* light ) if ( surf->shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) return; + // reject mirrors, portals, sky boxes, etc. if ( surf->shader->sort < SS_OPAQUE ) return; if ( surf->lightCount == tr.lightCount ) return; // already in the lit list (or already culled) for this light + const int stageIndex = surf->shader->lightingStages[ST_DIFFUSE]; + if ( stageIndex < 0 ) + return; + + const shaderStage_t* const stage = surf->shader->stages[stageIndex]; + const int srcBits = stage->stateBits & GLS_SRCBLEND_BITS; + const int dstBits = stage->stateBits & GLS_DSTBLEND_BITS; + + // we can't use a texture that was used with such a blend mode + // since the final color could look nothing like the texture itself + if ( srcBits == GLS_SRCBLEND_ONE_MINUS_DST_COLOR || + dstBits == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR ) + return; + surf->lightCount = tr.lightCount; if ( R_LightCullSurface( surf->data, light ) ) {