forked from Mirrors/openclonk
Add a directional shader for light rendering
This allows to render light as a cone in a certain direction with an inner circle that is illuminating all directions. It is basically implemented as a mask when rendering into the light texture. A separate fragment shader for the first of the three render passes is used to set the light intensity based on where the fragment is relative to the light source.directional-lights
parent
96013162fa
commit
cd2b5a0e39
|
@ -146,6 +146,7 @@ C4Shader *C4FoW::GetRenderShader()
|
|||
const char* szUniforms[C4FoWRSU_Count + 1];
|
||||
szUniforms[C4FoWRSU_ProjectionMatrix] = "projectionMatrix";
|
||||
szUniforms[C4FoWRSU_VertexOffset] = "vertexOffset";
|
||||
szUniforms[C4FoWRSU_LightSourcePosition] = "lightSourcePosition";
|
||||
szUniforms[C4FoWRSU_Count] = nullptr;
|
||||
|
||||
const char* szAttributes[C4FoWRSA_Count + 1];
|
||||
|
@ -165,6 +166,101 @@ C4Shader *C4FoW::GetRenderShader()
|
|||
#endif
|
||||
}
|
||||
|
||||
C4Shader *C4FoW::GetDirectionalRenderShader()
|
||||
{
|
||||
#ifndef USE_CONSOLE
|
||||
// Not created yet?
|
||||
if (!DirectionalRenderShader.Initialised())
|
||||
{
|
||||
// Create the directional render shader. Similar to the
|
||||
// normal render shader except discards pixels not in the
|
||||
// direction of the light (w/ fade-out).
|
||||
const char* DirectionalRenderVertexShader =
|
||||
"in vec2 oc_Position;\n"
|
||||
"in vec4 oc_Color;\n"
|
||||
"out vec4 vtxColor;\n"
|
||||
"out vec2 vtxLightDir;\n"
|
||||
"uniform mat4 projectionMatrix;\n"
|
||||
"uniform vec2 lightSourcePosition;\n"
|
||||
"\n"
|
||||
"slice(position)\n"
|
||||
"{\n"
|
||||
" gl_Position = projectionMatrix * vec4(oc_Position, 0.0, 1.0);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"slice(color)\n"
|
||||
"{\n"
|
||||
" vtxColor = oc_Color;\n"
|
||||
" vtxLightDir = (oc_Position - lightSourcePosition);\n"
|
||||
"}";
|
||||
|
||||
const char* DirectionalRenderFragmentShader =
|
||||
"in vec4 vtxColor;\n"
|
||||
"in vec2 vtxLightDir;\n"
|
||||
"out vec4 fragColor;\n"
|
||||
"\n"
|
||||
// TODO: these are going to be uniforms:
|
||||
"const vec2 lightDirection = vec2(1.0, 0.0);\n"
|
||||
"const float PI = 3.141592653589793238462643383;\n"
|
||||
"const float lightAngularRangeCos = cos(20.0 * PI / 180.0);\n" // 50 deg
|
||||
"const float lightAngularFadeCos = cos((20.0 + 30.0) * PI / 180.0);\n" // 80 deg
|
||||
"\n"
|
||||
"const float lightAngularDistance = 5.0;\n"
|
||||
"const float lightAngularDistanceFade = 5.0 + 15.0;\n"
|
||||
"\n"
|
||||
"slice(color)\n"
|
||||
"{\n"
|
||||
" float lightLen = sqrt(vtxLightDir.x * vtxLightDir.x + vtxLightDir.y * vtxLightDir.y);\n"
|
||||
" float angDiffCos = dot(lightDirection, vtxLightDir) / lightLen;\n"
|
||||
"\n"
|
||||
" float angOneMinusIntensity;\n"
|
||||
" if (angDiffCos <= lightAngularFadeCos)\n"
|
||||
" angOneMinusIntensity = 1.0;\n"
|
||||
//" float dist = min(1.0, 0.02 * sqrt(vtxLightDir.x * vtxLightDir.x + vtxLightDir.y * vtxLightDir.y));\n"
|
||||
//" fragColor = vec4(0.0, 0.5/1.5, 0.5/1.5, 1.0);\n"
|
||||
//" fragColor = vtxColor;\n"
|
||||
" else if (angDiffCos <= lightAngularRangeCos)\n"
|
||||
// TODO: this uses linear interpolation on the cosine values, which is not linear in the angle. Might need to use acos() here if things look funny.
|
||||
" angOneMinusIntensity = (lightAngularRangeCos - angDiffCos) / (lightAngularRangeCos - lightAngularFadeCos);\n"
|
||||
" else\n"
|
||||
" angOneMinusIntensity = 0.0;\n"
|
||||
"\n"
|
||||
" float distOneMinusIntensity;\n"
|
||||
" if (lightLen >= lightAngularDistanceFade)\n"
|
||||
" distOneMinusIntensity = 1.0;\n"
|
||||
" else if (lightLen >= lightAngularDistance)\n"
|
||||
" distOneMinusIntensity = (lightLen - lightAngularDistance) / (lightAngularDistanceFade - lightAngularDistance);\n"
|
||||
" else\n"
|
||||
" distOneMinusIntensity = 0.0;\n"
|
||||
"\n"
|
||||
" fragColor = vec4(vtxColor.rgb, (1.0 - distOneMinusIntensity*angOneMinusIntensity) * vtxColor.a);\n"
|
||||
"}";
|
||||
|
||||
DirectionalRenderShader.AddVertexSlices("built-in FoW directional render shader", DirectionalRenderVertexShader);
|
||||
DirectionalRenderShader.AddFragmentSlices("built-in FoW directional render shader", DirectionalRenderFragmentShader);
|
||||
|
||||
const char* szUniforms[C4FoWRSU_Count + 1];
|
||||
szUniforms[C4FoWRSU_ProjectionMatrix] = "projectionMatrix";
|
||||
szUniforms[C4FoWRSU_VertexOffset] = "vertexOffset";
|
||||
szUniforms[C4FoWRSU_LightSourcePosition] = "lightSourcePosition";
|
||||
szUniforms[C4FoWRSU_Count] = nullptr;
|
||||
|
||||
const char* szAttributes[C4FoWRSA_Count + 1];
|
||||
szAttributes[C4FoWRSA_Position] = "oc_Position";
|
||||
szAttributes[C4FoWRSA_Color] = "oc_Color";
|
||||
szAttributes[C4FoWRSA_Count] = nullptr;
|
||||
|
||||
if (!DirectionalRenderShader.Init("fowDirectionalRender", szUniforms, szAttributes)) {
|
||||
DirectionalRenderShader.ClearSlices();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
return &DirectionalRenderShader;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void C4FoW::Add(C4Object *pObj)
|
||||
{
|
||||
|
@ -249,9 +345,12 @@ void C4FoW::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen, C4Playe
|
|||
assert(pShader);
|
||||
if (!pShader) return;
|
||||
|
||||
C4Shader *pDirectionalShader = GetDirectionalRenderShader();
|
||||
assert(pDirectionalShader);
|
||||
if (!pDirectionalShader) return;
|
||||
|
||||
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
|
||||
if (pLight->IsVisibleForPlayer(pPlr))
|
||||
pLight->Render(pRegion, pOnScreen, projectionMatrix, *pShader);
|
||||
|
||||
pLight->Render(pRegion, pOnScreen, projectionMatrix, *pShader, *pDirectionalShader);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -81,8 +81,9 @@ enum C4FoWFramebufShaderAttributes {
|
|||
};
|
||||
|
||||
enum C4FoWRenderShaderUniforms {
|
||||
C4FoWRSU_ProjectionMatrix, // projection matrix
|
||||
C4FoWRSU_VertexOffset, // offset applied to vertex (TODO: could be encoded in projection matrix)
|
||||
C4FoWRSU_ProjectionMatrix, // projection matrix
|
||||
C4FoWRSU_VertexOffset, // offset applied to vertex (TODO: could be encoded in projection matrix)
|
||||
C4FoWRSU_LightSourcePosition, // position of the light source (in same coordinates as C4FoWRSA_Position passed into the shader)
|
||||
|
||||
C4FoWRSU_Count
|
||||
};
|
||||
|
@ -118,6 +119,8 @@ public:
|
|||
C4Shader *GetFramebufShader();
|
||||
// Shader to use for rendering the lights
|
||||
C4Shader *GetRenderShader();
|
||||
// Shader to use for rendering the lights
|
||||
C4Shader *GetDirectionalRenderShader();
|
||||
|
||||
void ClearDeletedLights();
|
||||
|
||||
|
@ -138,6 +141,7 @@ private:
|
|||
// Shader for updating the frame buffer
|
||||
C4Shader FramebufShader;
|
||||
C4Shader RenderShader;
|
||||
C4Shader DirectionalRenderShader;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -139,15 +139,21 @@ void C4FoWDrawLightTextureStrategy::Begin(const C4FoWRegion* regionPar)
|
|||
}
|
||||
}
|
||||
|
||||
void C4FoWDrawLightTextureStrategy::End(const StdProjectionMatrix& projectionMatrix, const C4Shader& renderShader)
|
||||
void C4FoWDrawLightTextureStrategy::End(const StdProjectionMatrix& projectionMatrix, const C4Shader& renderShader, const C4Shader& directionalRenderShader)
|
||||
{
|
||||
C4ShaderCall call(&renderShader);
|
||||
call.Start();
|
||||
call.SetUniformMatrix4x4(C4FoWRSU_ProjectionMatrix, projectionMatrix);
|
||||
|
||||
// If we have nothing to draw (e.g. directly after initialization), abort early.
|
||||
if (vertices.empty()) return;
|
||||
|
||||
C4ShaderCall directionalRenderCall(&directionalRenderShader);
|
||||
directionalRenderCall.Start();
|
||||
directionalRenderCall.SetUniformMatrix4x4(C4FoWRSU_ProjectionMatrix, projectionMatrix);
|
||||
|
||||
float y_offset[] = { 0.0f, 0.0f };
|
||||
directionalRenderCall.SetUniform2fv(C4FoWRSU_VertexOffset, 1, y_offset);
|
||||
|
||||
const float light_source_position[2] = { light->getX() - region->getRegion().x, light->getY() - region->getRegion().y };
|
||||
directionalRenderCall.SetUniform2fv(C4FoWRSU_LightSourcePosition, 1, light_source_position);
|
||||
|
||||
const GLuint vbo = bo[0];
|
||||
const GLuint ibo = bo[1];
|
||||
|
||||
|
@ -181,10 +187,6 @@ void C4FoWDrawLightTextureStrategy::End(const StdProjectionMatrix& projectionMat
|
|||
const float width = region->getSurfaceWidth();
|
||||
const float height = region->getSurfaceHeight() / 2.0;
|
||||
|
||||
// Set Y offset for vertex
|
||||
float y_offset[] = { 0.0f, 0.0f };
|
||||
call.SetUniform2fv(C4FoWRSU_VertexOffset, 1, y_offset);
|
||||
|
||||
// Enable scissor test to only draw in upper or lower half of texture
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(0, height, width, height);
|
||||
|
@ -197,10 +199,10 @@ void C4FoWDrawLightTextureStrategy::End(const StdProjectionMatrix& projectionMat
|
|||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
|
||||
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
|
||||
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
|
||||
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
|
||||
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r1)));
|
||||
glEnableVertexAttribArray(directionalRenderCall.GetAttribute(C4FoWRSA_Position));
|
||||
glEnableVertexAttribArray(directionalRenderCall.GetAttribute(C4FoWRSA_Color));
|
||||
glVertexAttribPointer(directionalRenderCall.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
|
||||
glVertexAttribPointer(directionalRenderCall.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r1)));
|
||||
}
|
||||
|
||||
// Set up blend equation, see C4FoWDrawLightTextureStrategy::DrawVertex
|
||||
|
@ -211,7 +213,14 @@ void C4FoWDrawLightTextureStrategy::End(const StdProjectionMatrix& projectionMat
|
|||
// Render 1st pass
|
||||
glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, 0);
|
||||
|
||||
directionalRenderCall.Finish();
|
||||
|
||||
// Prepare state for 2nd pass
|
||||
C4ShaderCall renderCall(&renderShader);
|
||||
renderCall.Start();
|
||||
renderCall.SetUniformMatrix4x4(C4FoWRSU_ProjectionMatrix, projectionMatrix);
|
||||
renderCall.SetUniform2fv(C4FoWRSU_VertexOffset, 1, y_offset);
|
||||
|
||||
//glBlendFunc(GL_ONE, GL_ONE);
|
||||
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
|
||||
|
||||
|
@ -221,10 +230,10 @@ void C4FoWDrawLightTextureStrategy::End(const StdProjectionMatrix& projectionMat
|
|||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
|
||||
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
|
||||
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
|
||||
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
|
||||
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r2)));
|
||||
glEnableVertexAttribArray(renderCall.GetAttribute(C4FoWRSA_Position));
|
||||
glEnableVertexAttribArray(renderCall.GetAttribute(C4FoWRSA_Color));
|
||||
glVertexAttribPointer(renderCall.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
|
||||
glVertexAttribPointer(renderCall.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r2)));
|
||||
}
|
||||
|
||||
// Render 2nd pass
|
||||
|
@ -235,7 +244,7 @@ void C4FoWDrawLightTextureStrategy::End(const StdProjectionMatrix& projectionMat
|
|||
glBlendEquation(GL_FUNC_ADD);
|
||||
glScissor(0, 0, width, height);
|
||||
y_offset[1] = height;
|
||||
call.SetUniform2fv(C4FoWRSU_VertexOffset, 1, y_offset);
|
||||
renderCall.SetUniform2fv(C4FoWRSU_VertexOffset, 1, y_offset);
|
||||
|
||||
const bool has_vao3 = pGL->GetVAO(vaoids[2], vao3);
|
||||
glBindVertexArray(vao3);
|
||||
|
@ -243,12 +252,12 @@ void C4FoWDrawLightTextureStrategy::End(const StdProjectionMatrix& projectionMat
|
|||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
|
||||
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Position));
|
||||
glEnableVertexAttribArray(call.GetAttribute(C4FoWRSA_Color));
|
||||
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
|
||||
glVertexAttribPointer(call.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r3)));
|
||||
glEnableVertexAttribArray(renderCall.GetAttribute(C4FoWRSA_Position));
|
||||
glEnableVertexAttribArray(renderCall.GetAttribute(C4FoWRSA_Color));
|
||||
glVertexAttribPointer(renderCall.GetAttribute(C4FoWRSA_Position), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, x)));
|
||||
glVertexAttribPointer(renderCall.GetAttribute(C4FoWRSA_Color), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<const uint8_t*>(offsetof(Vertex, r3)));
|
||||
}
|
||||
|
||||
|
||||
// Render 3rd pass
|
||||
glDrawElements(GL_TRIANGLES, triangulator.GetNIndices(), GL_UNSIGNED_INT, 0);
|
||||
|
||||
|
@ -258,7 +267,9 @@ void C4FoWDrawLightTextureStrategy::End(const StdProjectionMatrix& projectionMat
|
|||
|
||||
// Assume the capacity stays the same:
|
||||
vertices.resize(0);
|
||||
C4FoWDrawStrategy::End(projectionMatrix, renderShader);
|
||||
C4FoWDrawStrategy::End(projectionMatrix, renderShader, directionalRenderShader);
|
||||
|
||||
renderCall.Finish();
|
||||
}
|
||||
|
||||
void C4FoWDrawLightTextureStrategy::DrawVertex(float x, float y, bool shadow)
|
||||
|
@ -363,7 +374,7 @@ void C4FoWDrawWireframeStrategy::Begin(const C4FoWRegion* region)
|
|||
{
|
||||
}
|
||||
|
||||
void C4FoWDrawWireframeStrategy::End(const StdProjectionMatrix& projectionMatrix, const C4Shader& renderShader)
|
||||
void C4FoWDrawWireframeStrategy::End(const StdProjectionMatrix& projectionMatrix, const C4Shader& renderShader, const C4Shader& directionalRenderShader)
|
||||
{
|
||||
C4ShaderCall call(&renderShader);
|
||||
call.Start();
|
||||
|
@ -429,7 +440,7 @@ void C4FoWDrawWireframeStrategy::End(const StdProjectionMatrix& projectionMatrix
|
|||
|
||||
// Assume the capacity stays the same:
|
||||
vertices.resize(0);
|
||||
C4FoWDrawStrategy::End(projectionMatrix, renderShader);
|
||||
C4FoWDrawStrategy::End(projectionMatrix, renderShader, directionalRenderShader);
|
||||
}
|
||||
|
||||
void C4FoWDrawWireframeStrategy::DrawVertex(Vertex& vtx)
|
||||
|
|
|
@ -87,7 +87,7 @@ public:
|
|||
/* TODO: shaders should be created in drawstrategy, but: make sure
|
||||
* there is only one shader per class
|
||||
* (static? but then when to clean up?). */
|
||||
virtual void End(const StdProjectionMatrix&, const C4Shader& renderShader) { triangulator.Reset(); }
|
||||
virtual void End(const StdProjectionMatrix&, const C4Shader& renderShader, const C4Shader& directionalRenderShader) { triangulator.Reset(); }
|
||||
|
||||
virtual void DrawLightVertex(float x, float y) { triangulator.AddVertex(); }
|
||||
virtual void DrawDarkVertex(float x, float y) { triangulator.AddVertex(); }
|
||||
|
@ -127,7 +127,7 @@ public:
|
|||
virtual void DrawLightVertex(float x, float y);
|
||||
virtual void DrawDarkVertex(float x, float y);
|
||||
virtual void Begin(const C4FoWRegion* region);
|
||||
virtual void End(const StdProjectionMatrix&, const C4Shader& renderShader);
|
||||
virtual void End(const StdProjectionMatrix&, const C4Shader& renderShader, const C4Shader& directionalRenderShader);
|
||||
|
||||
private:
|
||||
void DrawVertex(float x, float y, bool shadeLight);
|
||||
|
@ -164,7 +164,7 @@ public:
|
|||
virtual void DrawLightVertex(float x, float y);
|
||||
virtual void DrawDarkVertex(float x, float y);
|
||||
virtual void Begin(const C4FoWRegion* region);
|
||||
virtual void End(const StdProjectionMatrix&, const C4Shader& renderShader);
|
||||
virtual void End(const StdProjectionMatrix&, const C4Shader& renderShader, const C4Shader& directionalRenderShader);
|
||||
|
||||
private:
|
||||
struct Vertex {
|
||||
|
|
|
@ -119,7 +119,7 @@ void C4FoWLight::Update(C4Rect Rec)
|
|||
sections[i]->Update(Rec);
|
||||
}
|
||||
|
||||
void C4FoWLight::Render(C4FoWRegion *region, const C4TargetFacet *onScreen, const StdProjectionMatrix& projectionMatrix, const C4Shader& renderShader)
|
||||
void C4FoWLight::Render(C4FoWRegion *region, const C4TargetFacet *onScreen, const StdProjectionMatrix& projectionMatrix, const C4Shader& renderShader, const C4Shader& directionalRenderShader)
|
||||
{
|
||||
TriangleList triangles;
|
||||
|
||||
|
@ -157,7 +157,7 @@ void C4FoWLight::Render(C4FoWRegion *region, const C4TargetFacet *onScreen, cons
|
|||
DrawFade(strategy.get(), triangles);
|
||||
DrawIntermediateFadeTriangles(strategy.get(), triangles);
|
||||
|
||||
strategy->End(projectionMatrix, renderShader);
|
||||
strategy->End(projectionMatrix, renderShader, directionalRenderShader);
|
||||
}
|
||||
|
||||
void C4FoWLight::CalculateFanMaxed(TriangleList &triangles) const
|
||||
|
|
|
@ -85,7 +85,7 @@ public:
|
|||
/** Update all light beams within the given rectangle for this light */
|
||||
void Update(C4Rect r);
|
||||
/** Render this light*/
|
||||
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen, const StdProjectionMatrix& projectionMatrix, const C4Shader& renderShader);
|
||||
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen, const StdProjectionMatrix& projectionMatrix, const C4Shader& renderShader, const C4Shader& directionalRenderShader);
|
||||
|
||||
bool IsVisibleForPlayer(C4Player *player) const; // check if attached to an object that is not hostile to the given player
|
||||
|
||||
|
|
Loading…
Reference in New Issue