Compare commits

...

2 Commits

Author SHA1 Message Date
Armin Burgmeier cd2b5a0e39 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.
2016-11-27 22:11:14 -08:00
Armin Burgmeier 96013162fa Instantiate C4ShaderCall within C4FoWDrawStrategy
This will allow us to use different shaders for different passes of the draw.
2016-11-27 22:11:14 -08:00
6 changed files with 156 additions and 36 deletions

View File

@ -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,14 +345,12 @@ void C4FoW::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen, C4Playe
assert(pShader);
if (!pShader) return;
C4ShaderCall Call(pShader);
Call.Start();
Call.SetUniformMatrix4x4(C4FoWRSU_ProjectionMatrix, projectionMatrix);
C4Shader *pDirectionalShader = GetDirectionalRenderShader();
assert(pDirectionalShader);
if (!pDirectionalShader) return;
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
if (pLight->IsVisibleForPlayer(pPlr))
pLight->Render(pRegion, pOnScreen, Call);
Call.Finish();
pLight->Render(pRegion, pOnScreen, projectionMatrix, *pShader, *pDirectionalShader);
#endif
}

View File

@ -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
};

View File

@ -139,11 +139,21 @@ void C4FoWDrawLightTextureStrategy::Begin(const C4FoWRegion* regionPar)
}
}
void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
void C4FoWDrawLightTextureStrategy::End(const StdProjectionMatrix& projectionMatrix, const C4Shader& renderShader, const C4Shader& directionalRenderShader)
{
// 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];
@ -177,10 +187,6 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
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);
@ -193,10 +199,10 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
{
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
@ -207,7 +213,14 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
// 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);
@ -217,10 +230,10 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
{
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
@ -231,7 +244,7 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
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);
@ -239,12 +252,12 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
{
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);
@ -254,7 +267,9 @@ void C4FoWDrawLightTextureStrategy::End(C4ShaderCall& call)
// Assume the capacity stays the same:
vertices.resize(0);
C4FoWDrawStrategy::End(call);
C4FoWDrawStrategy::End(projectionMatrix, renderShader, directionalRenderShader);
renderCall.Finish();
}
void C4FoWDrawLightTextureStrategy::DrawVertex(float x, float y, bool shadow)
@ -359,8 +374,12 @@ void C4FoWDrawWireframeStrategy::Begin(const C4FoWRegion* region)
{
}
void C4FoWDrawWireframeStrategy::End(C4ShaderCall& call)
void C4FoWDrawWireframeStrategy::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;
@ -421,7 +440,7 @@ void C4FoWDrawWireframeStrategy::End(C4ShaderCall& call)
// Assume the capacity stays the same:
vertices.resize(0);
C4FoWDrawStrategy::End(call);
C4FoWDrawStrategy::End(projectionMatrix, renderShader, directionalRenderShader);
}
void C4FoWDrawWireframeStrategy::DrawVertex(Vertex& vtx)

View File

@ -84,7 +84,10 @@ public:
/** Called before each rendering pass */
virtual void Begin(const C4FoWRegion* region) = 0;
/** Called after each rendering pass */
virtual void End(C4ShaderCall& call) { triangulator.Reset(); }
/* 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, const C4Shader& directionalRenderShader) { triangulator.Reset(); }
virtual void DrawLightVertex(float x, float y) { triangulator.AddVertex(); }
virtual void DrawDarkVertex(float x, float y) { triangulator.AddVertex(); }
@ -124,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(C4ShaderCall& call);
virtual void End(const StdProjectionMatrix&, const C4Shader& renderShader, const C4Shader& directionalRenderShader);
private:
void DrawVertex(float x, float y, bool shadeLight);
@ -161,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(C4ShaderCall& call);
virtual void End(const StdProjectionMatrix&, const C4Shader& renderShader, const C4Shader& directionalRenderShader);
private:
struct Vertex {

View File

@ -119,7 +119,7 @@ void C4FoWLight::Update(C4Rect Rec)
sections[i]->Update(Rec);
}
void C4FoWLight::Render(C4FoWRegion *region, const C4TargetFacet *onScreen, C4ShaderCall& call)
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, C4Sh
DrawFade(strategy.get(), triangles);
DrawIntermediateFadeTriangles(strategy.get(), triangles);
strategy->End(call);
strategy->End(projectionMatrix, renderShader, directionalRenderShader);
}
void C4FoWLight::CalculateFanMaxed(TriangleList &triangles) const

View File

@ -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, C4ShaderCall& call);
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