Added object renderer (WIP)
Rewritten shadow rendering logic Split some shaders into smaller parts Shadow renderer now has its own framebuffer objectdev
parent
ce71f25901
commit
57502d2f54
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue