Added object renderer (WIP)

Rewritten shadow rendering logic
Split some shaders into smaller parts
Shadow renderer now has its own framebuffer object
dev
Tomasz Kapuściński 2021-12-01 17:05:20 +01:00
parent ce71f25901
commit 57502d2f54
20 changed files with 1305 additions and 214 deletions

View File

@ -147,6 +147,8 @@ add_library(colobotbase STATIC
graphics/opengl/gl33device.h
graphics/opengl/gl33renderers.cpp
graphics/opengl/gl33renderers.h
graphics/opengl/gl33objectrenderer.cpp
graphics/opengl/gl33objectrenderer.h
graphics/opengl/glframebuffer.cpp
graphics/opengl/glframebuffer.h
graphics/opengl/glutil.cpp

View File

@ -53,6 +53,7 @@ class CFramebuffer;
class CUIRenderer;
class CTerrainRenderer;
class CShadowRenderer;
class CObjectRenderer;
struct FramebufferParams;
struct Light;
struct Material;
@ -455,6 +456,8 @@ public:
virtual CUIRenderer* GetUIRenderer() = 0;
//! Returns terrain renderer
virtual CTerrainRenderer* GetTerrainRenderer() = 0;
//! Returns object renderer
virtual CObjectRenderer* GetObjectRenderer() = 0;
//! Returns shadow renderer
virtual CShadowRenderer* GetShadowRenderer() = 0;

View File

@ -36,6 +36,21 @@ class CVertexBuffer;
enum PrimitiveType;
struct Texture;
enum class TransparencyMode
{
NONE,
ALPHA,
BLACK,
WHITE,
};
struct ShadowParam
{
glm::mat4 matrix;
glm::vec2 uv_offset;
glm::vec2 uv_scale;
};
/**
* \class CRenderer
* \brief Common abstract interface for renderers
@ -46,11 +61,11 @@ public:
virtual ~CRenderer() { }
//! Flush buffered content
virtual void Flush() = 0;
virtual void Flush() {}
};
/**
* \class CRenderer
* \class CUIRenderer
* \brief Abstract interface for UI renderers
*/
class CUIRenderer : public CRenderer
@ -69,6 +84,10 @@ public:
virtual void DrawPrimitive(PrimitiveType type, int count, const Vertex2D* vertices) = 0;
};
/**
* \class CTerrainRenderer
* \brief Abstract interface for terrain renderers
*/
class CTerrainRenderer : public CRenderer
{
public:
@ -84,8 +103,6 @@ public:
virtual void SetViewMatrix(const glm::mat4& matrix) = 0;
//! Sets model matrix
virtual void SetModelMatrix(const glm::mat4& matrix) = 0;
//! Sets shadow matrix
virtual void SetShadowMatrix(const glm::mat4& matrix) = 0;
//! Sets primary texture, setting texture 0 means using white texture
virtual void SetPrimaryTexture(const Texture& texture) = 0;
@ -96,6 +113,8 @@ public:
//! Sets light parameters
virtual void SetLight(const glm::vec4& position, const float& intensity, const glm::vec3& color) = 0;
//! Sets shadow parameters
virtual void SetShadowParams(int count, const ShadowParam* params) = 0;
//! Sets fog parameters
virtual void SetFog(float min, float max, const glm::vec3& color) = 0;
@ -104,6 +123,65 @@ public:
virtual void DrawObject(const glm::mat4& matrix, const CVertexBuffer* buffer) = 0;
};
/**
* \class CObjectRenderer
* \brief Abstract interface for object renderers
*/
class CObjectRenderer : public CRenderer
{
public:
virtual ~CObjectRenderer() { }
virtual void Begin() = 0;
virtual void End() = 0;
//! Sets projection matrix
virtual void SetProjectionMatrix(const glm::mat4& matrix) = 0;
//! Sets view matrix
virtual void SetViewMatrix(const glm::mat4& matrix) = 0;
//! Sets model matrix
virtual void SetModelMatrix(const glm::mat4& matrix) = 0;
//! Sets color
virtual void SetColor(const glm::vec4& color) = 0;
//! Sets primary texture
virtual void SetPrimaryTexture(const Texture& texture) = 0;
//! Sets secondary texture
virtual void SetSecondaryTexture(const Texture& texture) = 0;
//! Sets shadow map
virtual void SetShadowMap(const Texture& texture) = 0;
//! Enables lighting
virtual void SetLighting(bool enabled) = 0;
//! Sets light parameters
virtual void SetLight(const glm::vec4& position, const float& intensity, const glm::vec3& color) = 0;
//! Sets shadow parameters
virtual void SetShadowParams(int count, const ShadowParam* params) = 0;
//! Sets fog parameters
virtual void SetFog(float min, float max, const glm::vec3& color) = 0;
//! Sets alpha scissor
virtual void SetAlphaScissor(float alpha) = 0;
//! Sets cull mode
virtual void SetCullMode(bool enabled) = 0;
//! Sets transparency mode
virtual void SetTransparency(TransparencyMode mode) = 0;
virtual void SetPrimaryTextureEnabled(bool enabled) = 0;
//! Sets amount of dirt (second texture) to apply
virtual void SetDirty(float amount) = 0;
//! Draws an object
virtual void DrawObject(const CVertexBuffer* buffer) = 0;
};
/**
* \class CShadowRenderer
* \brief Abstract interface for shadow renderers
*/
class CShadowRenderer : public CRenderer
{
public:
@ -123,6 +201,11 @@ public:
//! Sets texture
virtual void SetTexture(const Texture& texture) = 0;
//! Sets shadow map
virtual void SetShadowMap(const Texture& texture) = 0;
//! Sets shadow region
virtual void SetShadowRegion(const glm::vec2& offset, const glm::vec2& scale) = 0;
//! Draws terrain object
virtual void DrawObject(const CVertexBuffer* buffer, bool transparent) = 0;
};

View File

@ -3359,9 +3359,9 @@ void CEngine::Draw3DScene()
// Display the objects
m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true);
m_device->SetRenderState(RENDER_STATE_LIGHTING, true);
m_device->SetRenderState(RENDER_STATE_FOG, true);
//m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true);
//m_device->SetRenderState(RENDER_STATE_LIGHTING, true);
//m_device->SetRenderState(RENDER_STATE_FOG, true);
float fogStart = m_deepView[m_rankView] * m_fogStart[m_rankView] * m_clippingDistance;
float fogEnd = m_deepView[m_rankView] * m_clippingDistance;
@ -3378,20 +3378,28 @@ void CEngine::Draw3DScene()
// Draw terrain
m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN);
//m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN);
UseShadowMapping(true);
SetState(0);
Gfx::ShadowParam shadowParams[4];
for (int i = 0; i < m_shadowRegions; i++)
{
shadowParams[i].matrix = m_shadowParams[i].transform;
shadowParams[i].uv_offset = m_shadowParams[i].offset;
shadowParams[i].uv_scale = m_shadowParams[i].scale;
}
auto terrainRenderer = m_device->GetTerrainRenderer();
terrainRenderer->Begin();
terrainRenderer->SetProjectionMatrix(m_matProj);
terrainRenderer->SetViewMatrix(m_matView);
terrainRenderer->SetShadowMatrix(m_shadowTextureMat);
terrainRenderer->SetShadowMap(m_shadowMap);
terrainRenderer->SetLight(glm::vec4(1.0, 1.0, -1.0, 0.0), 1.0f, glm::vec3(1.0));
terrainRenderer->SetShadowParams(m_shadowRegions, shadowParams);
Color fogColor = m_fogColor[m_rankView];
@ -3459,6 +3467,20 @@ void CEngine::Draw3DScene()
CProfiler::StartPerformanceCounter(PCNT_RENDER_OBJECTS);
auto objectRenderer = m_device->GetObjectRenderer();
objectRenderer->Begin();
objectRenderer->SetProjectionMatrix(m_matProj);
objectRenderer->SetViewMatrix(m_matView);
objectRenderer->SetShadowMap(m_shadowMap);
objectRenderer->SetLighting(true);
objectRenderer->SetLight(glm::vec4(1.0, 1.0, -1.0, 0.0), 1.0f, glm::vec3(1.0));
objectRenderer->SetTransparency(TransparencyMode::NONE);
objectRenderer->SetFog(fogStart, fogEnd, { fogColor.r, fogColor.g, fogColor.b });
objectRenderer->SetAlphaScissor(0.5f);
objectRenderer->SetShadowParams(m_shadowRegions, shadowParams);
bool transparent = false;
for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
@ -3472,7 +3494,6 @@ void CEngine::Draw3DScene()
if (! m_objects[objRank].drawWorld)
continue;
m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform);
auto combinedMatrix = Math::MultiplyMatrices(projectionViewMatrix, m_objects[objRank].transform);
if (! IsVisible(combinedMatrix, objRank))
@ -3488,14 +3509,16 @@ void CEngine::Draw3DScene()
if (! p1.used)
continue;
objectRenderer->SetModelMatrix(m_objects[objRank].transform);
m_lightMan->UpdateDeviceLights(m_objects[objRank].type);
for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
{
EngineBaseObjTexTier& p2 = p1.next[l2];
SetTexture(p2.tex1, 0);
SetTexture(p2.tex2, 1);
objectRenderer->SetPrimaryTexture(p2.tex1);
objectRenderer->SetSecondaryTexture(p2.tex2);
for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
{
@ -3507,16 +3530,32 @@ void CEngine::Draw3DScene()
continue;
}
SetMaterial(p3.material);
SetState(p3.state);
if ((p3.state & ENG_RSTATE_TCOLOR_BLACK)
|| (p3.state & ENG_RSTATE_TCOLOR_WHITE)
|| (p3.state & ENG_RSTATE_TCOLOR_ALPHA))
objectRenderer->SetPrimaryTextureEnabled(false);
else
objectRenderer->SetPrimaryTextureEnabled(true);
DrawObject(p3);
float dirty = ((p3.state & ENG_RSTATE_DUAL_BLACK) && m_dirty) ? 1.0 : 0.0;
objectRenderer->SetDirty(dirty);
auto color = p3.material.diffuse;
objectRenderer->SetColor({ color.r, color.g, color.b, 1.0 });
objectRenderer->SetCullMode((p3.state& ENG_RSTATE_2FACE) == 0);
objectRenderer->DrawObject(p3.buffer);
}
}
}
objectRenderer->End();
UseShadowMapping(false);
objectRenderer->Begin();
objectRenderer->SetLighting(false);
objectRenderer->SetTransparency(TransparencyMode::ALPHA);
objectRenderer->SetAlphaScissor(0.0f);
// Draw transparent objects
if (transparent)
@ -3535,7 +3574,6 @@ void CEngine::Draw3DScene()
if (! m_objects[objRank].drawWorld)
continue;
m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform);
auto combinedMatrix = Math::MultiplyMatrices(projectionViewMatrix, m_objects[objRank].transform);
if (! IsVisible(combinedMatrix, objRank))
@ -3551,14 +3589,14 @@ void CEngine::Draw3DScene()
if (! p1.used)
continue;
m_lightMan->UpdateDeviceLights(m_objects[objRank].type);
objectRenderer->SetModelMatrix(m_objects[objRank].transform);
for (int l2 = 0; l2 < static_cast<int>( p1.next.size() ); l2++)
{
EngineBaseObjTexTier& p2 = p1.next[l2];
SetTexture(p2.tex1, 0);
SetTexture(p2.tex2, 1);
objectRenderer->SetPrimaryTexture(p2.tex1);
objectRenderer->SetSecondaryTexture(p2.tex2);
for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++)
{
@ -3567,15 +3605,19 @@ void CEngine::Draw3DScene()
if (m_objects[objRank].transparency == 0.0f)
continue;
SetMaterial(p3.material);
SetState(tState, tColor);
DrawObject(p3);
float dirty = (p3.state & ENG_RSTATE_DUAL_BLACK) && m_dirty ? 1.0 : 0.0;
objectRenderer->SetDirty(dirty);
auto color = p3.material.diffuse;
objectRenderer->SetColor({ color.r, color.g, color.b, 0.5f });// -m_objects[objRank].transparency });
objectRenderer->SetCullMode((p3.state& ENG_RSTATE_2FACE) == 0);
objectRenderer->DrawObject(p3.buffer);
}
}
}
}
objectRenderer->End();
CProfiler::StopPerformanceCounter(PCNT_RENDER_OBJECTS);
m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN);
@ -3904,62 +3946,51 @@ void CEngine::RenderShadowMap()
CProfiler::StartPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
m_shadowRegions = 4;
m_shadowParams[0].range = 32.0;
m_shadowParams[0].offset = { 0.0, 0.0 };
m_shadowParams[0].scale = { 0.5, 0.5 };
m_shadowParams[1].range = 96.0;
m_shadowParams[1].offset = { 0.5, 0.0 };
m_shadowParams[1].scale = { 0.5, 0.5 };
m_shadowParams[2].range = 256.0;
m_shadowParams[2].offset = { 0.0, 0.5 };
m_shadowParams[2].scale = { 0.5, 0.5 };
m_shadowParams[3].range = 1024.0;
m_shadowParams[3].offset = { 0.5, 0.5 };
m_shadowParams[3].scale = { 0.5, 0.5 };
// If no shadow map texture exists, create it
if (m_shadowMap.id == 0)
{
int width = 256, height = 256;
int depth;
FramebufferParams params;
params.width = m_offscreenShadowRenderingResolution;
params.height = m_offscreenShadowRenderingResolution;
params.depth = 32;
params.colorAttachment = FramebufferParams::AttachmentType::None;
params.depthAttachment = FramebufferParams::AttachmentType::Texture;
if (m_offscreenShadowRendering)
CFramebuffer* framebuffer = m_device->CreateFramebuffer("shadow", params);
if (framebuffer == nullptr)
{
width = height = m_offscreenShadowRenderingResolution;
FramebufferParams params;
params.width = params.height = width;
params.depth = depth = 32;
params.colorAttachment = FramebufferParams::AttachmentType::None;
params.depthAttachment = FramebufferParams::AttachmentType::Texture;
CFramebuffer *framebuffer = m_device->CreateFramebuffer("shadow", params);
if (framebuffer == nullptr)
{
GetLogger()->Error("Could not create shadow mapping framebuffer, disabling dynamic shadows\n");
m_shadowMapping = false;
m_offscreenShadowRendering = false;
m_qualityShadows = false;
CProfiler::StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
return;
}
m_shadowMap.id = framebuffer->GetDepthTexture();
m_shadowMap.size = Math::IntPoint(width, height);
}
else
{
int min = Math::Min(m_size.x, m_size.y);
for (int i = 0; i < 16; i++)
{
if (min < (1 << i)) break;
width = height = 1 << i;
}
depth = m_app->GetInstance().GetVideoConfig().depthSize;
m_shadowMap = m_device->CreateDepthTexture(width, height, depth);
GetLogger()->Error("Could not create shadow mapping framebuffer, disabling dynamic shadows\n");
m_shadowMapping = false;
m_offscreenShadowRendering = false;
m_qualityShadows = false;
CProfiler::StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
return;
}
GetLogger()->Info("Created shadow map texture: %dx%d, depth %d\n", width, height, depth);
}
m_shadowMap.id = framebuffer->GetDepthTexture();
m_shadowMap.size = Math::IntPoint(params.width, params.height);
if (m_offscreenShadowRendering)
{
m_device->GetFramebuffer("shadow")->Bind();
GetLogger()->Info("Created shadow map texture: %dx%d, depth %d\n", params.width, params.height, params.depth);
}
m_device->Clear();
// change state to rendering shadow maps
m_device->SetColorMask(false, false, false, false);
m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true);
@ -3974,111 +4005,117 @@ void CEngine::RenderShadowMap()
m_device->SetDepthBias(2.0f, 8.0f);
m_device->SetViewport(0, 0, m_shadowMap.size.x, m_shadowMap.size.y);
// recompute matrices
Math::Vector worldUp(0.0f, 1.0f, 0.0f);
Math::Vector lightDir = Math::Vector(1.0f, 2.0f, -1.0f);
Math::Vector dir = m_lookatPt - m_eyePt;
dir.y = 0.0f;
dir.Normalize();
float dist = m_shadowRange;
float depth = 200.0f;
if (dist < 0.5f)
{
float scale = log(m_shadowMap.size.x) / log(2.0f) - 6.5f;
dist = 75.0f * scale;
}
Math::Vector pos = m_lookatPt + 0.25f * dist * dir;
{
// To prevent 'shadow shimmering', we ensure that the position only moves in texel-sized
// increments. To do this we transform the position to a space where the light's forward/right/up
// axes are aligned with the x/y/z axes (not necessarily in that order, and +/- signs don't matter).
Math::Matrix lightRotation;
Math::LoadViewMatrix(lightRotation, Math::Vector{}, lightDir, worldUp);
pos = Math::MatrixVectorMultiply(lightRotation, pos);
// ...then we round to the nearest worldUnitsPerTexel:
const float worldUnitsPerTexel = (dist * 2.0f) / m_shadowMap.size.x;
pos /= worldUnitsPerTexel;
pos.x = round(pos.x);
pos.y = round(pos.y);
pos.z = round(pos.z);
pos *= worldUnitsPerTexel;
// ...and convert back to world space.
pos = Math::MatrixVectorMultiply(lightRotation.Inverse(), pos);
}
Math::Vector lookAt = pos - lightDir;
Math::LoadOrthoProjectionMatrix(m_shadowProjMat, -dist, dist, -dist, dist, -depth, depth);
Math::LoadViewMatrix(m_shadowViewMat, pos, lookAt, worldUp);
Math::Matrix scaleMat;
Math::LoadScaleMatrix(scaleMat, Math::Vector(1.0f, 1.0f, -1.0f));
m_shadowViewMat = Math::MultiplyMatrices(scaleMat, m_shadowViewMat);
Math::Matrix temporary = Math::MultiplyMatrices(m_shadowProjMat, m_shadowViewMat);
m_shadowTextureMat = Math::MultiplyMatrices(m_shadowBias, temporary);
m_shadowViewMat = Math::MultiplyMatrices(scaleMat, m_shadowViewMat);
m_device->SetTransform(TRANSFORM_PROJECTION, m_shadowProjMat);
m_device->SetTransform(TRANSFORM_VIEW, m_shadowViewMat);
m_device->SetTexture(0, 0);
m_device->SetTexture(1, 0);
m_device->SetTexture(2, 0);
m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, true);
m_device->SetRenderState(RENDER_STATE_CULLING, false);
auto projectionViewMatrix = Math::MultiplyMatrices(m_shadowProjMat, m_shadowViewMat);
auto renderer = m_device->GetShadowRenderer();
renderer->Begin();
renderer->SetProjectionMatrix(m_shadowProjMat);
renderer->SetViewMatrix(m_shadowViewMat);
renderer->SetShadowMap(m_shadowMap);
renderer->SetShadowRegion({ 0.0, 0.0 }, { 1.0, 1.0 });
// render objects into shadow map
for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
m_device->Clear();
for (int region = 0; region < m_shadowRegions; region++)
{
if (!m_objects[objRank].used)
continue;
renderer->SetShadowRegion(
m_shadowParams[region].offset,
m_shadowParams[region].scale);
bool terrain = (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN);
// recompute matrices
Math::Vector worldUp(0.0f, 1.0f, 0.0f);
Math::Vector lightDir = Math::Vector(1.0f, 2.0f, -1.0f);
Math::Vector dir = m_lookatPt - m_eyePt;
dir.y = 0.0f;
dir.Normalize();
if (terrain && !m_terrainShadows) continue;
float range = m_shadowParams[region].range;
auto combinedMatrix = Math::MultiplyMatrices(projectionViewMatrix, m_objects[objRank].transform);
float dist = range;
float depth = 200.0f;
if (!IsVisible(combinedMatrix, objRank))
continue;
int baseObjRank = m_objects[objRank].baseObjRank;
if (baseObjRank == -1)
continue;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (!p1.used)
continue;
renderer->SetModelMatrix(m_objects[objRank].transform);
for (int l2 = 0; l2 < static_cast<int>(p1.next.size()); l2++)
if (dist < 0.5f)
{
EngineBaseObjTexTier& p2 = p1.next[l2];
float scale = log(m_shadowMap.size.x) / log(2.0f) - 6.5f;
dist = 75.0f * scale;
}
renderer->SetTexture(p2.tex1);
Math::Vector pos = m_lookatPt + 0.25f * dist * dir;
for (int l3 = 0; l3 < static_cast<int>(p2.next.size()); l3++)
{
// To prevent 'shadow shimmering', we ensure that the position only moves in texel-sized
// increments. To do this we transform the position to a space where the light's forward/right/up
// axes are aligned with the x/y/z axes (not necessarily in that order, and +/- signs don't matter).
Math::Matrix lightRotation;
Math::LoadViewMatrix(lightRotation, Math::Vector{}, lightDir, worldUp);
pos = Math::MatrixVectorMultiply(lightRotation, pos);
// ...then we round to the nearest worldUnitsPerTexel:
const float worldUnitsPerTexel = (dist * 2.0f) / m_shadowMap.size.x;
pos /= worldUnitsPerTexel;
pos.x = round(pos.x);
pos.y = round(pos.y);
pos.z = round(pos.z);
pos *= worldUnitsPerTexel;
// ...and convert back to world space.
pos = Math::MatrixVectorMultiply(lightRotation.Inverse(), pos);
}
Math::Vector lookAt = pos - lightDir;
Math::LoadOrthoProjectionMatrix(m_shadowProjMat, -dist, dist, -dist, dist, -depth, depth);
Math::LoadViewMatrix(m_shadowViewMat, pos, lookAt, worldUp);
Math::Matrix scaleMat;
Math::LoadScaleMatrix(scaleMat, Math::Vector(1.0f, 1.0f, -1.0f));
m_shadowViewMat = Math::MultiplyMatrices(scaleMat, m_shadowViewMat);
Math::Matrix temporary = Math::MultiplyMatrices(m_shadowProjMat, m_shadowViewMat);
m_shadowTextureMat = Math::MultiplyMatrices(m_shadowBias, temporary);
m_shadowViewMat = Math::MultiplyMatrices(scaleMat, m_shadowViewMat);
auto projectionViewMatrix = Math::MultiplyMatrices(m_shadowProjMat, m_shadowViewMat);
m_shadowParams[region].transform = m_shadowTextureMat;
renderer->SetProjectionMatrix(m_shadowProjMat);
renderer->SetViewMatrix(m_shadowViewMat);
// render objects into shadow map
for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
{
if (!m_objects[objRank].used)
continue;
bool terrain = (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN);
if (terrain && !m_terrainShadows) continue;
auto combinedMatrix = Math::MultiplyMatrices(projectionViewMatrix, m_objects[objRank].transform);
if (!IsVisible(combinedMatrix, objRank))
continue;
int baseObjRank = m_objects[objRank].baseObjRank;
if (baseObjRank == -1)
continue;
assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
EngineBaseObject& p1 = m_baseObjects[baseObjRank];
if (!p1.used)
continue;
renderer->SetModelMatrix(m_objects[objRank].transform);
for (int l2 = 0; l2 < static_cast<int>(p1.next.size()); l2++)
{
EngineBaseObjDataTier& p3 = p2.next[l3];
EngineBaseObjTexTier& p2 = p1.next[l2];
renderer->DrawObject(p3.buffer, true);
renderer->SetTexture(p2.tex1);
for (int l3 = 0; l3 < static_cast<int>(p2.next.size()); l3++)
{
EngineBaseObjDataTier& p3 = p2.next[l3];
renderer->DrawObject(p3.buffer, true);
}
}
}
}
@ -4091,14 +4128,8 @@ void CEngine::RenderShadowMap()
m_device->SetRenderState(RENDER_STATE_CULLING, false);
m_device->SetCullMode(CULL_CW);
if (m_offscreenShadowRendering) // shadow map texture already have depth information, just unbind it
{
m_device->GetFramebuffer("shadow")->Unbind();
}
else // copy depth buffer to shadow map
{
m_device->CopyFramebufferToTexture(m_shadowMap, 0, 0, 0, 0, m_shadowMap.size.x, m_shadowMap.size.y);
}
m_device->GetFramebuffer("default")->Bind();
//m_device->GetFramebuffer("shadow")->Unbind();
// restore default state
m_device->SetViewport(0, 0, m_size.x, m_size.y);

View File

@ -1423,6 +1423,16 @@ protected:
bool m_editIndentMode;
int m_editIndentValue;
struct ShadowParam
{
glm::mat4 transform;
glm::vec2 offset;
glm::vec2 scale;
float range;
};
int m_shadowRegions = 4;
ShadowParam m_shadowParams[4];
Texture m_shadowMap;
struct PendingDebugDraw

View File

@ -32,6 +32,7 @@
#include "graphics/opengl/glframebuffer.h"
#include "graphics/opengl/gl33renderers.h"
#include "graphics/opengl/gl33objectrenderer.h"
#include "math/geometry.h"
@ -434,6 +435,7 @@ bool CGL33Device::Create()
m_uiRenderer = std::make_unique<CGL33UIRenderer>(this);
m_terrainRenderer = std::make_unique<CGL33TerrainRenderer>(this);
m_objectRenderer = std::make_unique<CGL33ObjectRenderer>(this);
m_shadowRenderer = std::make_unique<CGL33ShadowRenderer>(this);
glUseProgram(m_normalProgram);
@ -556,6 +558,11 @@ CTerrainRenderer* CGL33Device::GetTerrainRenderer()
return m_terrainRenderer.get();
}
CObjectRenderer* CGL33Device::GetObjectRenderer()
{
return m_objectRenderer.get();
}
CShadowRenderer* CGL33Device::GetShadowRenderer()
{
return m_shadowRenderer.get();
@ -566,6 +573,7 @@ void CGL33Device::Restore()
m_uiRenderer->Flush();
glUseProgram(m_normalProgram);
//UpdateTextureState(0);
//UpdateTextureState(1);
//UpdateTextureState(2);

View File

@ -82,6 +82,7 @@ public:
class CGL33UIRenderer;
class CGL33TerrainRenderer;
class CGL33ObjectRenderer;
class CGL33ShadowRenderer;
/**
@ -118,6 +119,7 @@ public:
CUIRenderer* GetUIRenderer() override;
CTerrainRenderer* GetTerrainRenderer() override;
CObjectRenderer* GetObjectRenderer() override;
CShadowRenderer* GetShadowRenderer() override;
void Restore() override;
@ -309,6 +311,8 @@ private:
std::unique_ptr<CGL33UIRenderer> m_uiRenderer;
//! Terrain renderer
std::unique_ptr<CGL33TerrainRenderer> m_terrainRenderer;
//! Object renderer
std::unique_ptr<CGL33ObjectRenderer> m_objectRenderer;
//! Shadow renderer
std::unique_ptr<CGL33ShadowRenderer> m_shadowRenderer;
};

View File

@ -0,0 +1,334 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program 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.
*
* This program 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 this program. If not, see http://gnu.org/licenses
*/
#include "graphics/opengl/gl33objectrenderer.h"
#include "graphics/opengl/gl33device.h"
#include "graphics/opengl/glutil.h"
#include "graphics/core/vertex.h"
#include "common/logger.h"
#include <GL/glew.h>
#include <glm/gtc/type_ptr.hpp>
using namespace Gfx;
CGL33ObjectRenderer::CGL33ObjectRenderer(CGL33Device* device)
:m_device(device)
{
GetLogger()->Info("Creating CGL33ObjectRenderer\n");
std::string preamble = LoadSource("shaders/gl33/preamble.glsl");
std::string shadowSource = LoadSource("shaders/gl33/shadow.glsl");
std::string vsSource = LoadSource("shaders/gl33/object_vs.glsl");
std::string fsSource = LoadSource("shaders/gl33/object_fs.glsl");
GLint vsShader = CreateShader(GL_VERTEX_SHADER, { preamble, shadowSource, vsSource });
if (vsShader == 0)
{
GetLogger()->Error("Cound not create vertex shader from file 'object_vs.glsl'\n");
return;
}
GLint fsShader = CreateShader(GL_FRAGMENT_SHADER, { preamble, shadowSource, fsSource });
if (fsShader == 0)
{
GetLogger()->Error("Cound not create fragment shader from file 'object_fs.glsl'\n");
return;
}
m_program = LinkProgram({ vsShader, fsShader });
glDeleteShader(vsShader);
glDeleteShader(fsShader);
glUseProgram(m_program);
// Setup uniforms
auto identity = glm::identity<glm::mat4>();
m_projectionMatrix = glGetUniformLocation(m_program, "uni_ProjectionMatrix");
m_viewMatrix = glGetUniformLocation(m_program, "uni_ViewMatrix");
m_cameraMatrix = glGetUniformLocation(m_program, "uni_CameraMatrix");
m_shadowMatrix = glGetUniformLocation(m_program, "uni_ShadowMatrix");
m_modelMatrix = glGetUniformLocation(m_program, "uni_ModelMatrix");
m_normalMatrix = glGetUniformLocation(m_program, "uni_NormalMatrix");
m_lighting = glGetUniformLocation(m_program, "uni_Lighting");
m_lightPosition = glGetUniformLocation(m_program, "uni_LightPosition");
m_lightIntensity = glGetUniformLocation(m_program, "uni_LightIntensity");
m_lightColor = glGetUniformLocation(m_program, "uni_LightColor");
m_fogRange = glGetUniformLocation(m_program, "uni_FogRange");
m_fogColor = glGetUniformLocation(m_program, "uni_FogColor");
m_color = glGetUniformLocation(m_program, "uni_Color");
m_primaryEnabled = glGetUniformLocation(m_program, "uni_PrimaryEnabled");
m_dirty = glGetUniformLocation(m_program, "uni_Dirty");
m_alphaScissor = glGetUniformLocation(m_program, "uni_AlphaScissor");
m_shadowRegions = glGetUniformLocation(m_program, "uni_ShadowRegions");
GLchar name[64];
for (int i = 0; i < 4; i++)
{
sprintf(name, "uni_ShadowParam[%d].transform", i);
m_shadows[i].transform = glGetUniformLocation(m_program, name);
sprintf(name, "uni_ShadowParam[%d].uv_offset", i);
m_shadows[i].offset = glGetUniformLocation(m_program, name);
sprintf(name, "uni_ShadowParam[%d].uv_scale", i);
m_shadows[i].scale = glGetUniformLocation(m_program, name);
}
// Set texture units
auto texture = glGetUniformLocation(m_program, "uni_PrimaryTexture");
glUniform1i(texture, 10);
texture = glGetUniformLocation(m_program, "uni_SecondaryTexture");
glUniform1i(texture, 11);
texture = glGetUniformLocation(m_program, "uni_ShadowMap");
glUniform1i(texture, 12);
// White texture
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &m_whiteTexture);
glBindTexture(GL_TEXTURE_2D, m_whiteTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
glUseProgram(0);
GetLogger()->Info("CGL33ObjectRenderer created successfully\n");
}
CGL33ObjectRenderer::~CGL33ObjectRenderer()
{
glDeleteProgram(m_program);
glDeleteTextures(1, &m_whiteTexture);
}
void CGL33ObjectRenderer::CGL33ObjectRenderer::Begin()
{
glUseProgram(m_program);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glActiveTexture(GL_TEXTURE10);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE11);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE12);
glBindTexture(GL_TEXTURE_2D, 0);
m_primaryTexture = 0;
m_secondaryTexture = 0;
m_shadowMap = 0;
glDepthMask(GL_TRUE);
}
void CGL33ObjectRenderer::CGL33ObjectRenderer::End()
{
m_device->Restore();
glDepthMask(GL_TRUE);
}
void CGL33ObjectRenderer::SetProjectionMatrix(const glm::mat4& matrix)
{
glUniformMatrix4fv(m_projectionMatrix, 1, GL_FALSE, value_ptr(matrix));
}
void CGL33ObjectRenderer::SetViewMatrix(const glm::mat4& matrix)
{
glm::mat4 scale(1.0f);
scale[2][2] = -1.0f;
auto viewMatrix = scale * matrix;
auto cameraMatrix = glm::inverse(viewMatrix);
glUniformMatrix4fv(m_viewMatrix, 1, GL_FALSE, value_ptr(viewMatrix));
glUniformMatrix4fv(m_cameraMatrix, 1, GL_FALSE, value_ptr(cameraMatrix));
}
void CGL33ObjectRenderer::SetModelMatrix(const glm::mat4& matrix)
{
auto normalMatrix = glm::transpose(glm::inverse(glm::mat3(matrix)));
glUniformMatrix4fv(m_modelMatrix, 1, GL_FALSE, value_ptr(matrix));
glUniformMatrix3fv(m_normalMatrix, 1, GL_FALSE, value_ptr(normalMatrix));
}
void CGL33ObjectRenderer::SetColor(const glm::vec4& color)
{
glUniform4fv(m_color, 1, glm::value_ptr(color));
}
void CGL33ObjectRenderer::SetPrimaryTexture(const Texture& texture)
{
if (m_primaryTexture == texture.id) return;
m_primaryTexture = texture.id;
glActiveTexture(GL_TEXTURE10);
if (texture.id == 0)
glBindTexture(GL_TEXTURE_2D, m_whiteTexture);
else
glBindTexture(GL_TEXTURE_2D, texture.id);
}
void CGL33ObjectRenderer::SetSecondaryTexture(const Texture& texture)
{
if (m_secondaryTexture == texture.id) return;
m_secondaryTexture = texture.id;
glActiveTexture(GL_TEXTURE11);
if (texture.id == 0)
glBindTexture(GL_TEXTURE_2D, m_whiteTexture);
else
glBindTexture(GL_TEXTURE_2D, texture.id);
}
void CGL33ObjectRenderer::SetShadowMap(const Texture& texture)
{
if (m_shadowMap == texture.id) return;
m_shadowMap = texture.id;
glActiveTexture(GL_TEXTURE12);
if (texture.id == 0)
glBindTexture(GL_TEXTURE_2D, m_whiteTexture);
else
glBindTexture(GL_TEXTURE_2D, texture.id);
}
void CGL33ObjectRenderer::SetLighting(bool enabled)
{
glUniform1i(m_lighting, enabled ? 1 : 0);
}
void CGL33ObjectRenderer::SetLight(const glm::vec4& position, const float& intensity, const glm::vec3& color)
{
glUniform4fv(m_lightPosition, 1, glm::value_ptr(position));
glUniform1f(m_lightIntensity, intensity);
glUniform3fv(m_lightColor, 1, glm::value_ptr(color));
}
void CGL33ObjectRenderer::SetShadowParams(int count, const ShadowParam* params)
{
glUniform1i(m_shadowRegions, count);
for (int i = 0; i < count; i++)
{
glUniformMatrix4fv(m_shadows[i].transform, 1, GL_FALSE, glm::value_ptr(params[i].matrix));
glUniform2fv(m_shadows[i].offset, 1, glm::value_ptr(params[i].uv_offset));
glUniform2fv(m_shadows[i].scale, 1, glm::value_ptr(params[i].uv_scale));
}
}
void CGL33ObjectRenderer::SetFog(float min, float max, const glm::vec3& color)
{
glUniform2f(m_fogRange, min, max);
glUniform3f(m_fogColor, color.r, color.g, color.b);
}
void CGL33ObjectRenderer::SetCullMode(bool enabled)
{
if (enabled)
{
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
}
else
{
glDisable(GL_CULL_FACE);
}
}
void CGL33ObjectRenderer::SetTransparency(TransparencyMode mode)
{
switch (mode)
{
case TransparencyMode::NONE:
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
break;
case TransparencyMode::ALPHA:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
glDepthMask(GL_TRUE);
break;
case TransparencyMode::BLACK:
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
glBlendEquation(GL_FUNC_ADD);
glDepthMask(GL_TRUE);
break;
case TransparencyMode::WHITE:
glEnable(GL_BLEND);
glBlendFunc(GL_DST_COLOR, GL_ZERO);
glBlendEquation(GL_FUNC_ADD);
glDepthMask(GL_TRUE);
break;
}
}
void CGL33ObjectRenderer::SetPrimaryTextureEnabled(bool enabled)
{
glUniform1f(m_primaryEnabled, enabled ? 1.0f : 0.0f);
}
void CGL33ObjectRenderer::SetDirty(float amount)
{
glUniform1f(m_dirty, amount);
}
void CGL33ObjectRenderer::SetAlphaScissor(float alpha)
{
glUniform1f(m_alphaScissor, alpha);
}
void CGL33ObjectRenderer::DrawObject(const CVertexBuffer* buffer)
{
auto b = dynamic_cast<const CGL33VertexBuffer*>(buffer);
if (b == nullptr) return;
glBindVertexArray(b->GetVAO());
glDrawArrays(TranslateGfxPrimitive(b->GetType()), 0, b->Size());
}

View File

@ -0,0 +1,134 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program 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.
*
* This program 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 this program. If not, see http://gnu.org/licenses
*/
/**
* \file graphics/opengl/gl33objectrenderer.h
* \brief OpenGL 3.3 object renderer
*/
#pragma once
#include "graphics/core/renderers.h"
#include <gl/glew.h>
#include <array>
#include <vector>
// Graphics module namespace
namespace Gfx
{
class CGL33Device;
class CGL33ObjectRenderer : public CObjectRenderer
{
public:
CGL33ObjectRenderer(CGL33Device* device);
virtual ~CGL33ObjectRenderer();
virtual void Begin() override;
virtual void End() override;
//! Sets projection matrix
virtual void SetProjectionMatrix(const glm::mat4& matrix) override;
//! Sets view matrix
virtual void SetViewMatrix(const glm::mat4& matrix) override;
//! Sets model matrix
virtual void SetModelMatrix(const glm::mat4& matrix) override;
//! Sets color
virtual void SetColor(const glm::vec4& color) override;
//! Sets primary texture
virtual void SetPrimaryTexture(const Texture& texture) override;
//! Sets secondary texture
virtual void SetSecondaryTexture(const Texture& texture) override;
//! Sets shadow map
virtual void SetShadowMap(const Texture& texture) override;
//! Enables lighting
virtual void SetLighting(bool enabled) override;
//! Sets light parameters
virtual void SetLight(const glm::vec4& position, const float& intensity, const glm::vec3& color) override;
//! Sets shadow parameters
virtual void SetShadowParams(int count, const ShadowParam* params) override;
//! Sets fog parameters
virtual void SetFog(float min, float max, const glm::vec3& color) override;
//! Sets alpha scissor
virtual void SetAlphaScissor(float alpha) override;
//! Sets cull mode parameters
virtual void SetCullMode(bool enabled) override;
//! Sets transparency mode
virtual void SetTransparency(TransparencyMode mode) override;
virtual void SetPrimaryTextureEnabled(bool enabled) override;
//! Sets amount of dirt (second texture) to apply
virtual void SetDirty(float amount) override;
//! Draws an object
virtual void DrawObject(const CVertexBuffer* buffer) override;
private:
CGL33Device* const m_device;
// Uniform data
GLint m_projectionMatrix = -1;
GLint m_viewMatrix = -1;
GLint m_cameraMatrix = -1;
GLint m_shadowMatrix = -1;
GLint m_modelMatrix = -1;
GLint m_normalMatrix = -1;
GLint m_lighting = -1;
GLint m_lightPosition = -1;
GLint m_lightIntensity = -1;
GLint m_lightColor = -1;
GLint m_fogRange = -1;
GLint m_fogColor = -1;
GLint m_color = -1;
GLint m_primaryEnabled = -1;
GLint m_dirty = -1;
GLint m_alphaScissor = -1;
struct ShadowUniforms
{
GLint transform;
GLint offset;
GLint scale;
};
GLint m_shadowRegions;
ShadowUniforms m_shadows[4];
// Shader program
GLuint m_program = 0;
// 1x1 white texture
GLuint m_whiteTexture = 0;
// Currently bound primary texture
GLuint m_primaryTexture = 0;
// Currently bound secondary texture
GLuint m_secondaryTexture = 0;
// Currently bound shadow map
GLuint m_shadowMap = 0;
};
}

View File

@ -229,31 +229,34 @@ CGL33TerrainRenderer::CGL33TerrainRenderer(CGL33Device* device)
{
GetLogger()->Info("Creating CGL33TerrainRenderer\n");
GLint shaders[2] = {};
std::string preamble = LoadSource("shaders/gl33/preamble.glsl");
std::string shadowSource = LoadSource("shaders/gl33/shadow.glsl");
std::string vsSource = LoadSource("shaders/gl33/terrain_vs.glsl");
std::string fsSource = LoadSource("shaders/gl33/terrain_fs.glsl");
shaders[0] = LoadShader(GL_VERTEX_SHADER, "shaders/gl33/terrain_vs.glsl");
if (shaders[0] == 0)
GLint vsShader = CreateShader(GL_VERTEX_SHADER, { preamble, shadowSource, vsSource });
if (vsShader == 0)
{
GetLogger()->Error("Cound not create vertex shader from file 'terrain_vs.glsl'\n");
return;
}
shaders[1] = LoadShader(GL_FRAGMENT_SHADER, "shaders/gl33/terrain_fs.glsl");
if (shaders[1] == 0)
GLint fsShader = CreateShader(GL_FRAGMENT_SHADER, { preamble, shadowSource, fsSource });
if (fsShader == 0)
{
GetLogger()->Error("Cound not create fragment shader from file 'terrain_fs.glsl'\n");
GetLogger()->Error("Cound not create fragment shader from file 'terrain_vs.glsl'\n");
return;
}
m_program = LinkProgram(2, shaders);
m_program = LinkProgram({ vsShader, fsShader });
if (m_program == 0)
{
GetLogger()->Error("Cound not link shader program for terrain renderer\n");
return;
}
glDeleteShader(shaders[0]);
glDeleteShader(shaders[1]);
glDeleteShader(vsShader);
glDeleteShader(fsShader);
glUseProgram(m_program);
@ -272,6 +275,22 @@ CGL33TerrainRenderer::CGL33TerrainRenderer(CGL33Device* device)
m_fogRange = glGetUniformLocation(m_program, "uni_FogRange");
m_fogColor = glGetUniformLocation(m_program, "uni_FogColor");
m_shadowRegions = glGetUniformLocation(m_program, "uni_ShadowRegions");
GLchar name[64];
for (int i = 0; i < 4; i++)
{
sprintf(name, "uni_ShadowParam[%d].transform", i);
m_shadows[i].transform = glGetUniformLocation(m_program, name);
sprintf(name, "uni_ShadowParam[%d].uv_offset", i);
m_shadows[i].offset = glGetUniformLocation(m_program, name);
sprintf(name, "uni_ShadowParam[%d].uv_scale", i);
m_shadows[i].scale = glGetUniformLocation(m_program, name);
}
// Set texture units to 10th and 11th
auto texture = glGetUniformLocation(m_program, "uni_PrimaryTexture");
glUniform1i(texture, 10);
@ -310,10 +329,44 @@ CGL33TerrainRenderer::~CGL33TerrainRenderer()
void CGL33TerrainRenderer::Begin()
{
glUseProgram(m_program);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE10);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE11);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE12);
glBindTexture(GL_TEXTURE_2D, 0);
m_primaryTexture = 0;
m_secondaryTexture = 0;
m_shadowMap = 0;
}
void CGL33TerrainRenderer::End()
{
glActiveTexture(GL_TEXTURE10);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE11);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE12);
glBindTexture(GL_TEXTURE_2D, 0);
m_primaryTexture = 0;
m_secondaryTexture = 0;
m_shadowMap = 0;
m_device->Restore();
}
@ -342,11 +395,6 @@ void CGL33TerrainRenderer::SetModelMatrix(const glm::mat4& matrix)
glUniformMatrix3fv(m_normalMatrix, 1, GL_FALSE, value_ptr(normalMatrix));
}
void CGL33TerrainRenderer::SetShadowMatrix(const glm::mat4& matrix)
{
glUniformMatrix4fv(m_shadowMatrix, 1, GL_FALSE, value_ptr(matrix));
}
void CGL33TerrainRenderer::SetPrimaryTexture(const Texture& texture)
{
if (m_primaryTexture == texture.id) return;
@ -396,6 +444,18 @@ void CGL33TerrainRenderer::SetLight(const glm::vec4& position, const float& inte
glUniform3fv(m_lightColor, 1, glm::value_ptr(color));
}
void CGL33TerrainRenderer::SetShadowParams(int count, const ShadowParam* params)
{
glUniform1i(m_shadowRegions, count);
for (int i = 0; i < count; i++)
{
glUniformMatrix4fv(m_shadows[i].transform, 1, GL_FALSE, glm::value_ptr(params[i].matrix));
glUniform2fv(m_shadows[i].offset, 1, glm::value_ptr(params[i].uv_offset));
glUniform2fv(m_shadows[i].scale, 1, glm::value_ptr(params[i].uv_scale));
}
}
void CGL33TerrainRenderer::SetFog(float min, float max, const glm::vec3& color)
{
glUniform2f(m_fogRange, min, max);
@ -406,11 +466,7 @@ void CGL33TerrainRenderer::DrawObject(const glm::mat4& matrix, const CVertexBuff
{
auto b = dynamic_cast<const CGL33VertexBuffer*>(buffer);
if (b == nullptr)
{
GetLogger()->Error("No vertex buffer");
return;
}
if (b == nullptr) return;
SetModelMatrix(matrix);
glBindVertexArray(b->GetVAO());
@ -469,21 +525,34 @@ CGL33ShadowRenderer::CGL33ShadowRenderer(CGL33Device* device)
glUseProgram(0);
glGenFramebuffers(1, &m_framebuffer);
GetLogger()->Info("CGL33ShadowRenderer created successfully\n");
}
CGL33ShadowRenderer::~CGL33ShadowRenderer()
{
glDeleteProgram(m_program);
glDeleteFramebuffers(1, &m_framebuffer);
}
void CGL33ShadowRenderer::Begin()
{
glViewport(0, 0, m_width, m_height);
glDepthMask(GL_TRUE);
glClear(GL_DEPTH_BUFFER_BIT);
glUseProgram(m_program);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
}
void CGL33ShadowRenderer::End()
{
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 0, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
m_device->Restore();
}
@ -513,15 +582,36 @@ void CGL33ShadowRenderer::SetTexture(const Texture& texture)
glBindTexture(GL_TEXTURE_2D, texture.id);
}
void CGL33ShadowRenderer::SetShadowMap(const Texture& texture)
{
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture.id, 0);
m_width = texture.size.x;
m_height = texture.size.y;
auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
GetLogger()->Error("Framebuffer incomplete: %d\n", status);
}
}
void CGL33ShadowRenderer::SetShadowRegion(const glm::vec2& offset, const glm::vec2& scale)
{
int x = static_cast<int>(m_width * offset.x);
int y = static_cast<int>(m_height * offset.y);
int width = static_cast<int>(m_width * scale.x);
int height = static_cast<int>(m_height * scale.y);
glViewport(x, y, width, height);
}
void CGL33ShadowRenderer::DrawObject(const CVertexBuffer* buffer, bool transparent)
{
auto b = dynamic_cast<const CGL33VertexBuffer*>(buffer);
if (b == nullptr)
{
GetLogger()->Error("No vertex buffer");
return;
}
if (b == nullptr) return;
glUniform1i(m_alphaScissor, transparent ? 1 : 0);

View File

@ -108,8 +108,6 @@ public:
virtual void SetViewMatrix(const glm::mat4& matrix) override;
//! Sets model matrix
virtual void SetModelMatrix(const glm::mat4& matrix) override;
//! Sets shadow matrix
virtual void SetShadowMatrix(const glm::mat4& matrix) override;
//! Sets primary texture, setting texture 0 means using white texture
virtual void SetPrimaryTexture(const Texture& texture) override;
@ -121,6 +119,8 @@ public:
//! Sets light parameters
virtual void SetLight(const glm::vec4& position, const float& intensity, const glm::vec3& color) override;
virtual void SetShadowParams(int count, const ShadowParam* params) override;
//! Sets fog parameters
virtual void SetFog(float min, float max, const glm::vec3& color) override;
@ -145,6 +145,16 @@ private:
GLint m_fogRange;
GLint m_fogColor;
struct ShadowUniforms
{
GLint transform;
GLint offset;
GLint scale;
};
GLint m_shadowRegions;
ShadowUniforms m_shadows[4];
// Shader program
GLuint m_program = 0;
@ -178,6 +188,11 @@ public:
//! Sets texture
virtual void SetTexture(const Texture& texture) override;
//! Sets shadow map
virtual void SetShadowMap(const Texture& texture) override;
//! Sets shadow region
virtual void SetShadowRegion(const glm::vec2& offset, const glm::vec2& scale) override;
//! Draws terrain object
virtual void DrawObject(const CVertexBuffer* buffer, bool transparent) override;
@ -194,6 +209,11 @@ private:
// Shader program
GLuint m_program = 0;
// Framebuffer
GLuint m_framebuffer = 0;
int m_width = 0;
int m_height = 0;
};
} // namespace Gfx

View File

@ -122,13 +122,10 @@ bool CGLFramebuffer::Create()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, previous);

View File

@ -461,6 +461,61 @@ std::string GetLastShaderError()
return lastShaderError;
}
std::string LoadSource(const std::string& path)
{
PHYSFS_file* file = PHYSFS_openRead(path.c_str());
if (file == nullptr)
{
CLogger::GetInstance().Error("Cannot read shader source file\n");
CLogger::GetInstance().Error("Missing file \"%s\"\n", path);
return 0;
}
size_t len = PHYSFS_fileLength(file);
std::string source(len, ' ');
size_t length = PHYSFS_read(file, source.data(), 1, len);
PHYSFS_close(file);
return source;
}
GLint CreateShader(GLint type, const std::vector<std::string>& sources)
{
std::vector<const GLchar*> parts;
for (const auto& source : sources)
{
parts.push_back(source.c_str());
}
GLuint shader = glCreateShader(type);
glShaderSource(shader, parts.size(), parts.data(), nullptr);
glCompileShader(shader);
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE)
{
GLint len;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
auto message = MakeUniqueArray<GLchar>(len + 1);
glGetShaderInfoLog(shader, len + 1, nullptr, message.get());
GetLogger()->Error("Shader compilation error occurred!\n%s\n", message.get());
lastShaderError = std::string("Shader compilation error occurred!\n\n") + std::string(message.get());
glDeleteShader(shader);
return 0;
}
return shader;
}
GLint LoadShader(GLint type, const char* filename)
{
PHYSFS_file *file = PHYSFS_openRead(filename);
@ -540,6 +595,40 @@ GLint LinkProgram(int count, const GLint* shaders)
return program;
}
GLint LinkProgram(const std::vector<GLint>& shaders)
{
GLint program = glCreateProgram();
for (auto shader : shaders)
glAttachShader(program, shader);
glLinkProgram(program);
for (auto shader : shaders)
glDetachShader(program, shader);
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status != GL_TRUE)
{
GLint len;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
std::string message(len, ' ');
glGetProgramInfoLog(program, len + 1, nullptr, message.data());
GetLogger()->Error("Shader program linking error occurred!\n%s\n", message.c_str());
lastShaderError = std::string("Shader program linking error occurred!\n\n") + std::string(message.c_str());
glDeleteProgram(program);
return 0;
}
return program;
}
std::unique_ptr<CGLFrameBufferPixels> GetGLFrameBufferPixels(Math::IntPoint size)
{
auto pixels = MakeUnique<CGLFrameBufferPixels>(4 * size.x * size.y);

View File

@ -33,6 +33,7 @@
#include <string>
#include <memory>
#include <vector>
struct SDL_Surface;
@ -97,10 +98,16 @@ GLenum TranslateType(Type type);
std::string GetLastShaderError();
std::string LoadSource(const std::string& path);
GLint CreateShader(GLint type, const std::vector<std::string>& sources);
GLint LoadShader(GLint type, const char* filename);
GLint LinkProgram(int count, const GLint* shaders);
GLint LinkProgram(const std::vector<GLint>& shaders);
// TODO: Moved this here temporarily only to remove code duplication in CGLDeviceXX
struct PreparedTextureData
{

View File

@ -0,0 +1,138 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program 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.
*
* This program 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 this program. If not, see http://gnu.org/licenses
*/
// FRAGMENT SHADER - TERRAIN RENDERER
uniform bool uni_Lighting;
uniform mat4 uni_CameraMatrix;
uniform vec4 uni_LightPosition;
uniform float uni_LightIntensity;
uniform vec3 uni_LightColor;
uniform vec2 uni_FogRange;
uniform vec3 uni_FogColor;
uniform sampler2D uni_PrimaryTexture;
uniform sampler2D uni_SecondaryTexture;
uniform vec4 uni_Color;
uniform float uni_PrimaryEnabled;
uniform float uni_Dirty;
uniform float uni_AlphaScissor;
const float PI = 3.1415926;
in VertexData
{
vec4 Color;
vec2 TexCoord0;
vec2 TexCoord1;
vec3 Normal;
vec3 Position;
vec3 ShadowCoords[4];
} data;
out vec4 out_FragColor;
vec3 schlickFresnel(float LdH, float metalness, vec3 color)
{
vec3 f = mix(vec3(0.04), color, metalness);
return f + (1.0 - f) * pow(1.0 - LdH, 5.0);
}
float geomSmith(float dotProd, float roughness)
{
float k = (roughness + 1.0) * (roughness + 1.0) / 8.0;
float denom = dotProd * (1 - k) + k;
return 1.0 / denom;
}
float ggxDistribution(float NdH, float roughness)
{
float alpha2 = roughness * roughness * roughness * roughness;
float d = (NdH * NdH) * (alpha2 - 1) + 1.0;
return alpha2 / (PI * d * d);
}
vec3 PBR(vec3 position, vec3 color, vec3 normal, float roughness, float metalness)
{
vec3 diffuseBrdf = mix(color, vec3(0.0), metalness);
vec3 light = normalize(uni_LightPosition.xyz);
float lightIntensity = 1.0;
vec3 view = normalize(uni_CameraMatrix[3].xyz - position);
vec3 halfway = normalize(view + light);
float NdH = dot(normal, halfway);
float LdH = dot(light, halfway);
float NdL = max(dot(normal, light), 0.0);
float NdV = dot(normal, view);
vec3 specBrdf = 0.25 * ggxDistribution(NdH, roughness)
* schlickFresnel(LdH, metalness, color)
* geomSmith(NdL, roughness)
* geomSmith(NdV, roughness);
return (diffuseBrdf + PI * specBrdf) * lightIntensity * uni_LightColor * NdL;
}
void main()
{
vec4 albedo = data.Color * uni_Color;
vec4 primary = texture(uni_PrimaryTexture, data.TexCoord0);
primary = mix(vec4(1.0), primary, uni_PrimaryEnabled);
albedo *= primary;
vec3 dirty = texture(uni_SecondaryTexture, data.TexCoord1).rgb;
dirty = mix(vec3(1.0), dirty, uni_Dirty);
albedo.rgb *= dirty;
vec3 color = albedo.rgb;
if (uni_Lighting)
{
float roughness = 0.9;
float metalness = 0.0;
float shadow = CalculateShadow(data.ShadowCoords);
shadow = mix(0.5, 1.0, shadow);
vec3 normal = mix(-data.Normal, data.Normal, float(gl_FrontFacing));
vec3 lighting = PBR(data.Position, color.rgb, normal, roughness, metalness);
vec3 skyColor = vec3(1.0);
float skyIntensity = 0.25;
color = lighting * shadow * (1.0 - skyIntensity) + color.rgb * skyColor * skyIntensity;
}
float dist = length(uni_CameraMatrix[3].xyz - data.Position);
float fogAmount = clamp((dist - uni_FogRange.x) / (uni_FogRange.y - uni_FogRange.x), 0.0, 1.0);
color = mix(color, uni_FogColor, fogAmount);
if (albedo.a < uni_AlphaScissor) discard;
out_FragColor = vec4(color, albedo.a);
}

View File

@ -0,0 +1,56 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program 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.
*
* This program 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 this program. If not, see http://gnu.org/licenses
*/
// VERTEX SHADER - TERRAIN RENDERER
uniform mat4 uni_ProjectionMatrix;
uniform mat4 uni_ViewMatrix;
uniform mat4 uni_ModelMatrix;
uniform mat3 uni_NormalMatrix;
layout(location = 0) in vec4 in_VertexCoord;
layout(location = 1) in vec3 in_Normal;
layout(location = 2) in vec4 in_Color;
layout(location = 3) in vec2 in_TexCoord0;
layout(location = 4) in vec2 in_TexCoord1;
out VertexData
{
vec4 Color;
vec2 TexCoord0;
vec2 TexCoord1;
vec3 Normal;
vec3 Position;
vec3 ShadowCoords[4];
} data;
void main()
{
vec4 position = uni_ModelMatrix * in_VertexCoord;
vec4 eyeSpace = uni_ViewMatrix * position;
gl_Position = uni_ProjectionMatrix * eyeSpace;
data.Color = in_Color;
data.TexCoord0 = in_TexCoord0;
data.TexCoord1 = in_TexCoord1;
data.Normal = mat3(uni_NormalMatrix) * in_Normal;
//data.Distance = abs(eyeSpace.z);
data.Position = position.xyz;
data.ShadowCoords = ProjectShadows(position.xyz);
}

View File

@ -0,0 +1,21 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program 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.
*
* This program 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 this program. If not, see http://gnu.org/licenses
*/
// Common shader preamble
#version 330 core

View File

@ -0,0 +1,74 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program 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.
*
* This program 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 this program. If not, see http://gnu.org/licenses
*/
// Shadow mapping implementation
struct ShadowParam
{
mat4 transform;
vec2 uv_offset;
vec2 uv_scale;
};
uniform sampler2DShadow uni_ShadowMap;
uniform int uni_ShadowRegions;
uniform ShadowParam uni_ShadowParam[4];
// Projects world-space position into an array of shadow coordinates
vec3[4] ProjectShadows(vec3 position)
{
vec3 result[4];
for (int i = 0; i < uni_ShadowRegions; i++)
{
vec4 projected = uni_ShadowParam[i].transform * vec4(position, 1.0);
result[i] = projected.xyz / projected.w;
}
return result;
}
// Calculates shadow based on array of shadow coordinates
float CalculateShadow(vec3 projected[4])
{
for (int i = 0; i < uni_ShadowRegions; i++)
{
if (projected[i].x < 0.0 || projected[i].x > 1.0) continue;
if (projected[i].y < 0.0 || projected[i].y > 1.0) continue;
vec2 uv = projected[i].xy * uni_ShadowParam[i].uv_scale + uni_ShadowParam[i].uv_offset;
float depth = projected[i].z;
vec3 coord = vec3(uv, depth);
/*
float value = texture(uni_ShadowMap, coord);
value += textureOffset(uni_ShadowMap, coord, ivec2( 1, 0));
value += textureOffset(uni_ShadowMap, coord, ivec2(-1, 0));
value += textureOffset(uni_ShadowMap, coord, ivec2( 0, 1));
value += textureOffset(uni_ShadowMap, coord, ivec2( 0,-1));
return value * (1.0f / 5.0f);
// */
return texture(uni_ShadowMap, coord);
}
return 1.0;
}

View File

@ -18,7 +18,6 @@
*/
// FRAGMENT SHADER - TERRAIN RENDERER
#version 330 core
uniform mat4 uni_CameraMatrix;
uniform vec4 uni_LightPosition;
@ -30,7 +29,6 @@ uniform vec3 uni_FogColor;
uniform sampler2D uni_PrimaryTexture;
uniform sampler2D uni_SecondaryTexture;
uniform sampler2DShadow uni_ShadowMap;
const float PI = 3.1415926;
@ -40,8 +38,8 @@ in VertexData
vec2 TexCoord0;
vec2 TexCoord1;
vec3 Normal;
vec4 ShadowCoord;
vec3 Position;
vec3 ShadowCoords[4];
} data;
out vec4 out_FragColor;
@ -100,12 +98,7 @@ void main()
float roughness = 0.7;
float metalness = 0.0;
float value = texture(uni_ShadowMap, data.ShadowCoord.xyz);
value += textureOffset(uni_ShadowMap, data.ShadowCoord.xyz, ivec2( 1, 0));
value += textureOffset(uni_ShadowMap, data.ShadowCoord.xyz, ivec2(-1, 0));
value += textureOffset(uni_ShadowMap, data.ShadowCoord.xyz, ivec2( 0, 1));
value += textureOffset(uni_ShadowMap, data.ShadowCoord.xyz, ivec2( 0,-1));
float shadow = value * (1.0f / 5.0f);
float shadow = CalculateShadow(data.ShadowCoords);
shadow = mix(0.5, 1.0, shadow);

View File

@ -18,12 +18,10 @@
*/
// VERTEX SHADER - TERRAIN RENDERER
#version 330 core
uniform mat4 uni_ProjectionMatrix;
uniform mat4 uni_ViewMatrix;
uniform mat4 uni_ModelMatrix;
uniform mat4 uni_ShadowMatrix;
uniform mat3 uni_NormalMatrix;
layout(location = 0) in vec4 in_VertexCoord;
@ -38,8 +36,8 @@ out VertexData
vec2 TexCoord0;
vec2 TexCoord1;
vec3 Normal;
vec4 ShadowCoord;
vec3 Position;
vec3 ShadowCoords[4];
} data;
void main()
@ -47,13 +45,12 @@ void main()
vec4 position = uni_ModelMatrix * in_VertexCoord;
vec4 eyeSpace = uni_ViewMatrix * position;
gl_Position = uni_ProjectionMatrix * eyeSpace;
vec4 shadowCoord = uni_ShadowMatrix * position;
data.Color = in_Color;
data.TexCoord0 = in_TexCoord0;
data.TexCoord1 = in_TexCoord1;
data.Normal = mat3(uni_NormalMatrix) * in_Normal;
data.ShadowCoord = vec4(shadowCoord.xyz / shadowCoord.w, 1.0f);
//data.Distance = abs(eyeSpace.z);
data.Position = position.xyz;
data.ShadowCoords = ProjectShadows(position.xyz);
}