mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-07 03:12:36 +01:00
Improve graphics performance
+ Add Framebuffer rendering to store data + Re-implement water using projected grid aproach
This commit is contained in:
parent
2985a70354
commit
30e059a0b6
@ -11,6 +11,7 @@
|
|||||||
#include <render/OpenGLRenderer.hpp>
|
#include <render/OpenGLRenderer.hpp>
|
||||||
#include "MapRenderer.hpp"
|
#include "MapRenderer.hpp"
|
||||||
#include "TextRenderer.hpp"
|
#include "TextRenderer.hpp"
|
||||||
|
#include "WaterRenderer.hpp"
|
||||||
|
|
||||||
class Model;
|
class Model;
|
||||||
class ModelFrame;
|
class ModelFrame;
|
||||||
@ -77,10 +78,16 @@ class GameRenderer
|
|||||||
|
|
||||||
/** Camera values passed to renderWorld() */
|
/** Camera values passed to renderWorld() */
|
||||||
ViewCamera _camera;
|
ViewCamera _camera;
|
||||||
|
|
||||||
|
GLuint framebufferName;
|
||||||
|
GLuint fbTextures[2];
|
||||||
|
GLuint fbRenderBuffers[1];
|
||||||
|
Renderer::ShaderProgram* postProg;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
GameRenderer(GameWorld*);
|
GameRenderer(GameWorld*);
|
||||||
|
~GameRenderer();
|
||||||
|
|
||||||
/** Number of issued draw calls */
|
/** Number of issued draw calls */
|
||||||
size_t rendered;
|
size_t rendered;
|
||||||
@ -92,7 +99,6 @@ public:
|
|||||||
/** @todo Clean up all these shader program and location variables */
|
/** @todo Clean up all these shader program and location variables */
|
||||||
Renderer::ShaderProgram* worldProg;
|
Renderer::ShaderProgram* worldProg;
|
||||||
Renderer::ShaderProgram* skyProg;
|
Renderer::ShaderProgram* skyProg;
|
||||||
Renderer::ShaderProgram* waterProg;
|
|
||||||
Renderer::ShaderProgram* particleProg;
|
Renderer::ShaderProgram* particleProg;
|
||||||
|
|
||||||
GLuint ssRectProgram;
|
GLuint ssRectProgram;
|
||||||
@ -181,7 +187,10 @@ public:
|
|||||||
return renderer;
|
return renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setViewport(int w, int h);
|
||||||
|
|
||||||
MapRenderer map;
|
MapRenderer map;
|
||||||
|
WaterRenderer water;
|
||||||
TextRenderer text;
|
TextRenderer text;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,15 +2,26 @@
|
|||||||
#ifndef _GAMESHADERS_HPP_
|
#ifndef _GAMESHADERS_HPP_
|
||||||
#define _GAMESHADERS_HPP_
|
#define _GAMESHADERS_HPP_
|
||||||
|
|
||||||
|
#define SHADER_VF(Name) \
|
||||||
|
struct Name {\
|
||||||
|
static const char* VertexShader;\
|
||||||
|
static const char* FragmentShader;\
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief collection of shaders to make managing them a little easier.
|
* @brief collection of shaders to make managing them a little easier.
|
||||||
*/
|
*/
|
||||||
namespace GameShaders {
|
namespace GameShaders {
|
||||||
|
|
||||||
struct WaterHQ {
|
/**
|
||||||
static const char* VertexShader;
|
* High Quality Projected-Grid water shader
|
||||||
static const char* FragmentShader;
|
*/
|
||||||
};
|
SHADER_VF(WaterHQ)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple 3D masking shader
|
||||||
|
*/
|
||||||
|
SHADER_VF(Mask3D)
|
||||||
|
|
||||||
struct Sky {
|
struct Sky {
|
||||||
static const char* VertexShader;
|
static const char* VertexShader;
|
||||||
@ -37,6 +48,8 @@ struct ScreenSpaceRect {
|
|||||||
static const char* FragmentShader;
|
static const char* FragmentShader;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SHADER_VF(DefaultPostProcess);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -79,6 +79,8 @@ public:
|
|||||||
|
|
||||||
const AttributeList& getDataAttributes() const
|
const AttributeList& getDataAttributes() const
|
||||||
{ return attributes; }
|
{ return attributes; }
|
||||||
|
AttributeList& getDataAttributes()
|
||||||
|
{ return attributes; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
48
rwengine/include/render/WaterRenderer.hpp
Normal file
48
rwengine/include/render/WaterRenderer.hpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <engine/RWTypes.hpp>
|
||||||
|
#include <render/OpenGLRenderer.hpp>
|
||||||
|
|
||||||
|
class GameRenderer;
|
||||||
|
class GameWorld;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the rendering routines for drawing the sea water.
|
||||||
|
*/
|
||||||
|
class WaterRenderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaterRenderer(GameRenderer* renderer);
|
||||||
|
~WaterRenderer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the required data for rendering the water. Accepts
|
||||||
|
* two arrays. waterHeights stores the real world heights which
|
||||||
|
* are indexed into by the array tiles for each water tile.
|
||||||
|
*
|
||||||
|
* This data is used to create the internal stencil mask for clipping
|
||||||
|
* the water rendering.
|
||||||
|
*/
|
||||||
|
void setWaterTable(float* waterHeights, unsigned int nHeights, uint8_t* tiles, unsigned int nTiles);
|
||||||
|
|
||||||
|
void setDataTexture(GLuint fbBinding, GLuint dataTexture);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the water using the currently active render state
|
||||||
|
*/
|
||||||
|
void render(GameRenderer* renderer, GameWorld* world);
|
||||||
|
private:
|
||||||
|
Renderer::ShaderProgram* waterProg;
|
||||||
|
Renderer::ShaderProgram* maskProg;
|
||||||
|
|
||||||
|
DrawBuffer maskDraw;
|
||||||
|
GeometryBuffer maskGeom;
|
||||||
|
|
||||||
|
std::vector<int> maskSizes;
|
||||||
|
|
||||||
|
DrawBuffer gridDraw;
|
||||||
|
GeometryBuffer gridGeom;
|
||||||
|
|
||||||
|
GLuint fbOutput;
|
||||||
|
GLuint dataTexture;
|
||||||
|
};
|
@ -37,20 +37,6 @@ struct WaterVertex {
|
|||||||
float x, y;
|
float x, y;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<WaterVertex> waterLQVerts = {
|
|
||||||
{1.0f, 1.0f},
|
|
||||||
{0.0f, 1.0f},
|
|
||||||
{1.0f,-0.0f},
|
|
||||||
{0.0f,-0.0f}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<WaterVertex> waterHQVerts;
|
|
||||||
|
|
||||||
GeometryBuffer waterLQBuffer;
|
|
||||||
DrawBuffer waterLQDraw;
|
|
||||||
GeometryBuffer waterHQBuffer;
|
|
||||||
DrawBuffer waterHQDraw;
|
|
||||||
|
|
||||||
/// @todo collapse all of these into "VertPNC" etc.
|
/// @todo collapse all of these into "VertPNC" etc.
|
||||||
struct ParticleVert {
|
struct ParticleVert {
|
||||||
static const AttributeList vertex_attributes() {
|
static const AttributeList vertex_attributes() {
|
||||||
@ -81,7 +67,7 @@ DrawBuffer ssRectDraw;
|
|||||||
|
|
||||||
GameRenderer::GameRenderer(GameWorld* engine)
|
GameRenderer::GameRenderer(GameWorld* engine)
|
||||||
: engine(engine), renderer(new OpenGLRenderer), _renderAlpha(0.f),
|
: engine(engine), renderer(new OpenGLRenderer), _renderAlpha(0.f),
|
||||||
map(engine, renderer), text(engine, this)
|
map(engine, renderer), water(this), text(engine, this)
|
||||||
{
|
{
|
||||||
engine->logger.info("Renderer", renderer->getIDString());
|
engine->logger.info("Renderer", renderer->getIDString());
|
||||||
|
|
||||||
@ -102,49 +88,44 @@ GameRenderer::GameRenderer(GameWorld* engine)
|
|||||||
renderer->setProgramBlockBinding(particleProg, "ObjectData", 2);
|
renderer->setProgramBlockBinding(particleProg, "ObjectData", 2);
|
||||||
|
|
||||||
skyProg = renderer->createShader(
|
skyProg = renderer->createShader(
|
||||||
GameShaders::Sky::VertexShader,
|
GameShaders::Sky::VertexShader,
|
||||||
GameShaders::Sky::FragmentShader);
|
GameShaders::Sky::FragmentShader);
|
||||||
|
|
||||||
renderer->setProgramBlockBinding(skyProg, "SceneData", 1);
|
renderer->setProgramBlockBinding(skyProg, "SceneData", 1);
|
||||||
|
|
||||||
waterProg = renderer->createShader(
|
postProg = renderer->createShader(
|
||||||
GameShaders::WaterHQ::VertexShader,
|
GameShaders::DefaultPostProcess::VertexShader,
|
||||||
GameShaders::WaterHQ::FragmentShader);
|
GameShaders::DefaultPostProcess::FragmentShader);
|
||||||
|
|
||||||
renderer->setUniformTexture(waterProg, "texture", 0);
|
|
||||||
|
|
||||||
renderer->setProgramBlockBinding(waterProg, "SceneData", 1);
|
|
||||||
renderer->setProgramBlockBinding(waterProg, "ObjectData", 2);
|
|
||||||
|
|
||||||
glGenVertexArrays( 1, &vao );
|
glGenVertexArrays( 1, &vao );
|
||||||
|
|
||||||
|
glGenFramebuffers(1, &framebufferName);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
|
||||||
|
glGenTextures(2, fbTextures);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, fbTextures[0]);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, fbTextures[1]);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, 128, 128, 0, GL_RED, GL_FLOAT, NULL);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
// Upload water plane
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbTextures[0], 0);
|
||||||
waterLQBuffer.uploadVertices(waterLQVerts);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fbTextures[1], 0);
|
||||||
waterLQDraw.addGeometry(&waterLQBuffer);
|
|
||||||
waterLQDraw.setFaceType(GL_TRIANGLE_STRIP);
|
// Give water renderer the data texture
|
||||||
|
water.setDataTexture(1, fbTextures[1]);
|
||||||
// Generate HQ water geometry
|
|
||||||
int waterverts = 5;
|
glGenRenderbuffers(1, fbRenderBuffers);
|
||||||
float vertStep = 1.f/waterverts;
|
glBindRenderbuffer(GL_RENDERBUFFER, fbRenderBuffers[0]);
|
||||||
for(int x = 0; x < waterverts; ++x) {
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 128, 128);
|
||||||
float xB = vertStep * x;
|
glFramebufferRenderbuffer(
|
||||||
for(int y = 0; y < waterverts; ++y) {
|
GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbRenderBuffers[0]
|
||||||
float yB = vertStep * y;
|
);
|
||||||
waterHQVerts.push_back({xB + vertStep, yB + vertStep});
|
|
||||||
waterHQVerts.push_back({xB, yB + vertStep});
|
|
||||||
waterHQVerts.push_back({xB + vertStep, yB });
|
|
||||||
|
|
||||||
waterHQVerts.push_back({xB + vertStep, yB });
|
|
||||||
waterHQVerts.push_back({xB, yB + vertStep});
|
|
||||||
waterHQVerts.push_back({xB, yB });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
waterHQBuffer.uploadVertices(waterHQVerts);
|
|
||||||
waterHQDraw.addGeometry(&waterHQBuffer);
|
|
||||||
waterHQDraw.setFaceType(GL_TRIANGLES);
|
|
||||||
|
|
||||||
|
|
||||||
// Create the skydome
|
// Create the skydome
|
||||||
|
|
||||||
size_t segments = skydomeSegments, rows = skydomeRows;
|
size_t segments = skydomeSegments, rows = skydomeRows;
|
||||||
@ -199,6 +180,7 @@ GameRenderer::GameRenderer(GameWorld* engine)
|
|||||||
|
|
||||||
ssRectGeom.uploadVertices(sspaceRect);
|
ssRectGeom.uploadVertices(sspaceRect);
|
||||||
ssRectDraw.addGeometry(&ssRectGeom);
|
ssRectDraw.addGeometry(&ssRectGeom);
|
||||||
|
ssRectDraw.setFaceType(GL_TRIANGLE_STRIP);
|
||||||
|
|
||||||
ssRectProgram = compileProgram(GameShaders::ScreenSpaceRect::VertexShader,
|
ssRectProgram = compileProgram(GameShaders::ScreenSpaceRect::VertexShader,
|
||||||
GameShaders::ScreenSpaceRect::FragmentShader);
|
GameShaders::ScreenSpaceRect::FragmentShader);
|
||||||
@ -233,6 +215,11 @@ GameRenderer::GameRenderer(GameWorld* engine)
|
|||||||
cylinderBuffer.setFaceType(GL_TRIANGLES);
|
cylinderBuffer.setFaceType(GL_TRIANGLES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GameRenderer::~GameRenderer()
|
||||||
|
{
|
||||||
|
glDeleteFramebuffers(1, &framebufferName);
|
||||||
|
}
|
||||||
|
|
||||||
float mix(uint8_t a, uint8_t b, float num)
|
float mix(uint8_t a, uint8_t b, float num)
|
||||||
{
|
{
|
||||||
return a+(b-a)*num;
|
return a+(b-a)*num;
|
||||||
@ -248,6 +235,8 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha)
|
|||||||
// Set the viewport
|
// Set the viewport
|
||||||
const glm::ivec2& vp = getRenderer()->getViewport();
|
const glm::ivec2& vp = getRenderer()->getViewport();
|
||||||
glViewport(0, 0, vp.x, vp.y);
|
glViewport(0, 0, vp.x, vp.y);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
glBindVertexArray( vao );
|
glBindVertexArray( vao );
|
||||||
|
|
||||||
@ -394,76 +383,7 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha)
|
|||||||
}
|
}
|
||||||
glDepthMask(GL_TRUE);
|
glDepthMask(GL_TRUE);
|
||||||
|
|
||||||
// Draw the water.
|
water.render(this, engine);
|
||||||
renderer->useProgram( waterProg );
|
|
||||||
|
|
||||||
float blockLQSize = WATER_WORLD_SIZE/WATER_LQ_DATA_SIZE;
|
|
||||||
float blockHQSize = WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE;
|
|
||||||
|
|
||||||
glm::vec2 waterOffset { -WATER_WORLD_SIZE/2.f, -WATER_WORLD_SIZE/2.f };
|
|
||||||
auto waterTex = engine->gameData.findTexture("water_old");
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
|
|
||||||
auto camposFlat = glm::vec2(camera.position);
|
|
||||||
|
|
||||||
Renderer::DrawParameters wdp;
|
|
||||||
wdp.start = 0;
|
|
||||||
wdp.count = waterHQVerts.size();
|
|
||||||
wdp.texture = waterTex->getName();
|
|
||||||
|
|
||||||
renderer->useProgram(waterProg);
|
|
||||||
renderer->setSceneParameters(sceneParams);
|
|
||||||
|
|
||||||
// Draw High detail water
|
|
||||||
renderer->setUniform(waterProg, "size", blockHQSize);
|
|
||||||
renderer->setUniform(waterProg, "time", engine->gameTime);
|
|
||||||
renderer->setUniform(waterProg, "waveParams", glm::vec2(WATER_SCALE, WATER_HEIGHT));
|
|
||||||
|
|
||||||
for( int x = 0; x < WATER_HQ_DATA_SIZE; x++ ) {
|
|
||||||
for( int y = 0; y < WATER_HQ_DATA_SIZE; y++ ) {
|
|
||||||
auto waterWS = waterOffset + glm::vec2(blockHQSize) * glm::vec2(x, y);
|
|
||||||
auto cullWS = waterWS + (blockHQSize / 2.f);
|
|
||||||
|
|
||||||
// Check that this is the right time to draw the HQ water
|
|
||||||
if( glm::distance(camposFlat, cullWS) - blockHQSize >= WATER_HQ_DISTANCE ) continue;
|
|
||||||
|
|
||||||
int i = (x*WATER_HQ_DATA_SIZE) + y;
|
|
||||||
int hI = engine->gameData.realWater[i];
|
|
||||||
if( hI >= NO_WATER_INDEX ) continue;
|
|
||||||
float h = engine->gameData.waterHeights[hI];
|
|
||||||
|
|
||||||
glm::mat4 m;
|
|
||||||
m = glm::translate(m, glm::vec3(waterWS, h));
|
|
||||||
|
|
||||||
renderer->drawArrays(m, &waterHQDraw, wdp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
wdp.count = waterLQVerts.size();
|
|
||||||
renderer->setUniform(waterProg, "size", blockLQSize);
|
|
||||||
renderer->setUniform(waterProg, "waveParams", glm::vec2(0.f));
|
|
||||||
|
|
||||||
for( int x = 0; x < WATER_LQ_DATA_SIZE; x++ ) {
|
|
||||||
for( int y = 0; y < WATER_LQ_DATA_SIZE; y++ ) {
|
|
||||||
auto waterWS = waterOffset + glm::vec2(blockLQSize) * glm::vec2(x, y);
|
|
||||||
auto cullWS = waterWS + (blockLQSize / 2.f);
|
|
||||||
|
|
||||||
// Check that this is the right time to draw the LQ
|
|
||||||
if( glm::distance(camposFlat, cullWS) - blockHQSize/4.f < WATER_HQ_DISTANCE ) continue;
|
|
||||||
if( glm::distance(camposFlat, cullWS) - blockLQSize/2.f > camera.frustum.far ) continue;
|
|
||||||
|
|
||||||
int i = (x*WATER_LQ_DATA_SIZE) + y;
|
|
||||||
int hI = engine->gameData.visibleWater[i];
|
|
||||||
if( hI >= NO_WATER_INDEX ) continue;
|
|
||||||
float h = engine->gameData.waterHeights[hI];
|
|
||||||
|
|
||||||
glm::mat4 m;
|
|
||||||
m = glm::translate(m, glm::vec3(waterWS, h));
|
|
||||||
|
|
||||||
renderer->drawArrays(m, &waterLQDraw, wdp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindVertexArray( vao );
|
glBindVertexArray( vao );
|
||||||
|
|
||||||
@ -531,6 +451,18 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha)
|
|||||||
if( (engine->state.isCinematic || engine->state.currentCutscene ) && splashTexName == 0 ) {
|
if( (engine->state.isCinematic || engine->state.currentCutscene ) && splashTexName == 0 ) {
|
||||||
renderLetterbox();
|
renderLetterbox();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
|
|
||||||
|
renderer->useProgram(postProg);
|
||||||
|
|
||||||
|
Renderer::DrawParameters wdp;
|
||||||
|
wdp.start = 0;
|
||||||
|
wdp.count = ssRectGeom.getCount();
|
||||||
|
wdp.texture = fbTextures[0];
|
||||||
|
|
||||||
|
renderer->drawArrays(glm::mat4(), &ssRectDraw, wdp);
|
||||||
|
|
||||||
glUseProgram(0);
|
glUseProgram(0);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
@ -1205,3 +1137,19 @@ void GameRenderer::renderLetterbox()
|
|||||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameRenderer::setViewport(int w, int h)
|
||||||
|
{
|
||||||
|
auto& lastViewport = renderer->getViewport();
|
||||||
|
if( lastViewport.x != w || lastViewport.y != h)
|
||||||
|
{
|
||||||
|
renderer->setViewport({w, h});
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, fbTextures[0]);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, fbTextures[1]);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, w, h, 0, GL_RED, GL_FLOAT, NULL);
|
||||||
|
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, fbRenderBuffers[0]);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,26 +21,43 @@ layout(std140) uniform SceneData {
|
|||||||
float fogEnd;
|
float fogEnd;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(std140) uniform ObjectData {
|
|
||||||
mat4 model;
|
|
||||||
vec4 colour;
|
|
||||||
float diffusefac;
|
|
||||||
float ambientfac;
|
|
||||||
float visibility;
|
|
||||||
};
|
|
||||||
|
|
||||||
uniform float size;
|
|
||||||
|
|
||||||
uniform float time;
|
uniform float time;
|
||||||
uniform vec2 waveParams;
|
uniform vec2 waveParams;
|
||||||
|
uniform sampler2D data;
|
||||||
|
|
||||||
|
vec3 waterNormal = vec3(0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
vec3 planeIntercept( vec3 start, vec3 dir, float height )
|
||||||
|
{
|
||||||
|
float dist = (height - dot(waterNormal, start)) / dot(dir, waterNormal);
|
||||||
|
if( dist < 0.0 )
|
||||||
|
{
|
||||||
|
return start + dir * dist;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// uh oh
|
||||||
|
return vec3(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
mat4 MVP = projection * view;
|
TexCoords = position * vec2(0.5,0.5) + vec2(0.5);
|
||||||
vec4 vp = model * vec4(position * size, 0.0, 1.0);
|
|
||||||
vp.z = (1.0+sin(time + (vp.x + vp.y) * waveParams.x)) * waveParams.y;
|
mat4 vp = projection * view;
|
||||||
TexCoords = position * 2.0;
|
mat4 projector = inverse(vp);
|
||||||
gl_Position = MVP * vp;
|
|
||||||
|
mat3 rot = mat3(view);
|
||||||
|
vec3 ray = vec3(-position.x, -position.y, projection[0][0] ) * rot;
|
||||||
|
|
||||||
|
float plane = texture2D( data, TexCoords ).r;
|
||||||
|
|
||||||
|
vec3 ws = planeIntercept( campos.xyz, ray, plane );
|
||||||
|
|
||||||
|
ws.z = ws.z + (-1.0+(sin(time + (ws.x + ws.y) * waveParams.x)) * waveParams.y);
|
||||||
|
TexCoords = ws.xy / 5.0;
|
||||||
|
gl_Position = vp * vec4(ws, 1.0);
|
||||||
})";
|
})";
|
||||||
|
|
||||||
const char* WaterHQ::FragmentShader = R"(
|
const char* WaterHQ::FragmentShader = R"(
|
||||||
@ -49,11 +66,46 @@ in vec3 Normal;
|
|||||||
in vec2 TexCoords;
|
in vec2 TexCoords;
|
||||||
uniform sampler2D texture;
|
uniform sampler2D texture;
|
||||||
out vec4 outColour;
|
out vec4 outColour;
|
||||||
|
in vec3 test;
|
||||||
void main() {
|
void main() {
|
||||||
vec4 c = texture2D(texture, TexCoords);
|
vec4 c = texture2D(texture, TexCoords);
|
||||||
outColour = c;
|
outColour = c;
|
||||||
})";
|
})";
|
||||||
|
|
||||||
|
const char* Mask3D::VertexShader = R"(
|
||||||
|
#version 130
|
||||||
|
#extension GL_ARB_explicit_attrib_location : enable
|
||||||
|
#extension GL_ARB_uniform_buffer_object : enable
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 position;
|
||||||
|
|
||||||
|
layout(std140) uniform SceneData {
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
vec4 ambient;
|
||||||
|
vec4 dynamic;
|
||||||
|
vec4 fogColor;
|
||||||
|
vec4 campos;
|
||||||
|
float fogStart;
|
||||||
|
float fogEnd;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec3 pp;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
pp = position;
|
||||||
|
gl_Position = projection * view * vec4(position, 1.0);
|
||||||
|
})";
|
||||||
|
|
||||||
|
const char* Mask3D::FragmentShader = R"(
|
||||||
|
#version 130
|
||||||
|
in vec3 pp;
|
||||||
|
out vec4 outColour;
|
||||||
|
void main() {
|
||||||
|
outColour = vec4(pp.z, 0.0, 0.0, 1.0);
|
||||||
|
})";
|
||||||
|
|
||||||
const char* Sky::VertexShader = R"(
|
const char* Sky::VertexShader = R"(
|
||||||
#version 130
|
#version 130
|
||||||
#extension GL_ARB_explicit_attrib_location : enable
|
#extension GL_ARB_explicit_attrib_location : enable
|
||||||
@ -260,4 +312,33 @@ void main()
|
|||||||
// Set colour to 0, 0, 0, 1 for textured mode.
|
// Set colour to 0, 0, 0, 1 for textured mode.
|
||||||
outColour = vec4(colour.rgb + c.rgb, colour.a);
|
outColour = vec4(colour.rgb + c.rgb, colour.a);
|
||||||
})";
|
})";
|
||||||
}
|
|
||||||
|
const char* DefaultPostProcess::VertexShader = R"(
|
||||||
|
#version 130
|
||||||
|
#extension GL_ARB_explicit_attrib_location : enable
|
||||||
|
#extension GL_ARB_uniform_buffer_object : enable
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 position;
|
||||||
|
out vec2 TexCoords;
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
TexCoords = position * vec2(0.5,0.5) + vec2(0.5);
|
||||||
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
|
})";
|
||||||
|
|
||||||
|
const char* DefaultPostProcess::FragmentShader = R"(
|
||||||
|
#version 130
|
||||||
|
in vec2 TexCoords;
|
||||||
|
|
||||||
|
uniform sampler2D colour;
|
||||||
|
uniform sampler2D data;
|
||||||
|
|
||||||
|
out vec4 outColour;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 c = texture2D(colour, TexCoords);
|
||||||
|
outColour = c;
|
||||||
|
})";
|
||||||
|
|
||||||
|
}
|
159
rwengine/src/render/WaterRenderer.cpp
Normal file
159
rwengine/src/render/WaterRenderer.cpp
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
#include <render/WaterRenderer.hpp>
|
||||||
|
#include <render/GameRenderer.hpp>
|
||||||
|
#include <render/GameShaders.hpp>
|
||||||
|
#include <engine/GameWorld.hpp>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
WaterRenderer::WaterRenderer(GameRenderer* renderer)
|
||||||
|
: waterProg(nullptr)
|
||||||
|
{
|
||||||
|
maskDraw.setFaceType(GL_TRIANGLES);
|
||||||
|
gridDraw.setFaceType(GL_TRIANGLES);
|
||||||
|
|
||||||
|
waterProg = renderer->getRenderer()->createShader(
|
||||||
|
GameShaders::WaterHQ::VertexShader, GameShaders::WaterHQ::FragmentShader
|
||||||
|
);
|
||||||
|
maskProg = renderer->getRenderer()->createShader(
|
||||||
|
GameShaders::Mask3D::VertexShader, GameShaders::Mask3D::FragmentShader
|
||||||
|
);
|
||||||
|
|
||||||
|
renderer->getRenderer()->setProgramBlockBinding(waterProg, "SceneData", 1);
|
||||||
|
renderer->getRenderer()->setProgramBlockBinding(maskProg, "SceneData", 1);
|
||||||
|
|
||||||
|
renderer->getRenderer()->setUniformTexture(waterProg, "data", 1);
|
||||||
|
|
||||||
|
// Generate grid mesh
|
||||||
|
int gridres = 60;
|
||||||
|
std::vector<glm::vec2> grid;
|
||||||
|
float gridresinv = 1.f / (gridres*0.5f);
|
||||||
|
glm::vec2 b(-1.f,-1.f);
|
||||||
|
for( int x = 0; x < gridres; x++ )
|
||||||
|
{
|
||||||
|
for( int y = 0; y < gridres; y++ )
|
||||||
|
{
|
||||||
|
glm::vec2 tMin( b + glm::vec2(x,y) * gridresinv );
|
||||||
|
glm::vec2 tMax( b + glm::vec2(x+1,y+1) * gridresinv );
|
||||||
|
|
||||||
|
// Build geometry
|
||||||
|
grid.push_back(glm::vec2(tMax.x, tMax.y));
|
||||||
|
grid.push_back(glm::vec2(tMax.x, tMin.y));
|
||||||
|
grid.push_back(glm::vec2(tMin.x, tMin.y));
|
||||||
|
|
||||||
|
grid.push_back(glm::vec2(tMin.x, tMin.y));
|
||||||
|
grid.push_back(glm::vec2(tMin.x, tMax.y));
|
||||||
|
grid.push_back(glm::vec2(tMax.x, tMax.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gridGeom.uploadVertices(grid.size(), sizeof(glm::vec2)*grid.size(), grid.data());
|
||||||
|
gridGeom.getDataAttributes().push_back(
|
||||||
|
{ATRS_Position, 2, 0, 0, GL_FLOAT}
|
||||||
|
);
|
||||||
|
gridDraw.addGeometry(&gridGeom);
|
||||||
|
}
|
||||||
|
|
||||||
|
WaterRenderer::~WaterRenderer()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaterRenderer::setWaterTable(float* waterHeights, unsigned int nHeights, uint8_t* tiles, unsigned int nTiles)
|
||||||
|
{
|
||||||
|
// Determine the dimensions of the input tiles
|
||||||
|
int edgeNum = sqrt(nTiles);
|
||||||
|
float tileSize = WATER_WORLD_SIZE/edgeNum;
|
||||||
|
glm::vec2 wO { -WATER_WORLD_SIZE/2.f, -WATER_WORLD_SIZE/2.f };
|
||||||
|
|
||||||
|
std::vector<glm::vec3> vertexData;
|
||||||
|
|
||||||
|
for( int x = 0; x < edgeNum; x++ )
|
||||||
|
{
|
||||||
|
int xi = x * WATER_HQ_DATA_SIZE;
|
||||||
|
for( int y = 0; y < edgeNum; y++ )
|
||||||
|
{
|
||||||
|
// Tiles with the magic value contain no water.
|
||||||
|
if( tiles[xi + y] >= NO_WATER_INDEX ) continue;
|
||||||
|
float h = waterHeights[tiles[xi + y]];
|
||||||
|
float hMax = h + WATER_HEIGHT;
|
||||||
|
glm::vec2 tMin( wO + glm::vec2(x,y) * tileSize );
|
||||||
|
glm::vec2 tMax( wO + glm::vec2(x+1,y+1) * tileSize );
|
||||||
|
|
||||||
|
// Build geometry
|
||||||
|
vertexData.push_back(glm::vec3(tMax.x, tMax.y, hMax));
|
||||||
|
vertexData.push_back(glm::vec3(tMax.x, tMin.y, hMax));
|
||||||
|
vertexData.push_back(glm::vec3(tMin.x, tMin.y, hMax));
|
||||||
|
|
||||||
|
vertexData.push_back(glm::vec3(tMin.x, tMin.y, hMax));
|
||||||
|
vertexData.push_back(glm::vec3(tMin.x, tMax.y, hMax));
|
||||||
|
vertexData.push_back(glm::vec3(tMax.x, tMax.y, hMax));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maskGeom.uploadVertices(vertexData.size(), sizeof(glm::vec3)*vertexData.size(), vertexData.data());
|
||||||
|
maskGeom.getDataAttributes().push_back(
|
||||||
|
{ATRS_Position, 3, 0, 0, GL_FLOAT}
|
||||||
|
);
|
||||||
|
maskDraw.addGeometry(&maskGeom);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaterRenderer::setDataTexture(GLuint fbBinding, GLuint dataTex)
|
||||||
|
{
|
||||||
|
fbOutput = fbBinding;
|
||||||
|
dataTexture = dataTex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaterRenderer::render(GameRenderer* renderer, GameWorld* world)
|
||||||
|
{
|
||||||
|
auto r = renderer->getRenderer();
|
||||||
|
|
||||||
|
auto waterTex = world->gameData.findTexture("water_old");
|
||||||
|
|
||||||
|
Renderer::DrawParameters wdp;
|
||||||
|
wdp.start = 0;
|
||||||
|
wdp.count = maskGeom.getCount();
|
||||||
|
wdp.texture = 0;
|
||||||
|
glm::mat4 m(1.0);
|
||||||
|
|
||||||
|
glEnable(GL_STENCIL_TEST);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
glStencilFunc(GL_ALWAYS, 1, 0xFF);
|
||||||
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||||
|
glStencilMask(0xFF);
|
||||||
|
|
||||||
|
GLenum buffers[] = {GL_COLOR_ATTACHMENT1};
|
||||||
|
glDrawBuffers(1, buffers);
|
||||||
|
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
r->useProgram( maskProg );
|
||||||
|
|
||||||
|
|
||||||
|
r->drawArrays(m, &maskDraw, wdp);
|
||||||
|
|
||||||
|
glStencilFunc(GL_EQUAL, 1, 0xFF);
|
||||||
|
glStencilMask(0x00);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
r->useProgram( waterProg );
|
||||||
|
|
||||||
|
buffers[0] = GL_COLOR_ATTACHMENT0;
|
||||||
|
glDrawBuffers(1, buffers);
|
||||||
|
|
||||||
|
r->setUniform(waterProg, "time", world->gameTime);
|
||||||
|
r->setUniform(waterProg, "waveParams", glm::vec2(WATER_SCALE, WATER_HEIGHT));
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, dataTexture);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
wdp.count = gridGeom.getCount();
|
||||||
|
wdp.texture = waterTex->getName();
|
||||||
|
|
||||||
|
r->drawArrays(m, &gridDraw, wdp);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
|
glDisable(GL_STENCIL_TEST);
|
||||||
|
}
|
@ -93,6 +93,8 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
StateManager::get().enter(new LoadingState(this));
|
StateManager::get().enter(new LoadingState(this));
|
||||||
|
|
||||||
|
getRenderer()->water.setWaterTable(engine->gameData.waterHeights, 48, engine->gameData.realWater, 128*128);
|
||||||
|
|
||||||
engine->logger.info("Game", "Started");
|
engine->logger.info("Game", "Started");
|
||||||
}
|
}
|
||||||
@ -247,7 +249,7 @@ void RWGame::tick(float dt)
|
|||||||
void RWGame::render(float alpha, float time)
|
void RWGame::render(float alpha, float time)
|
||||||
{
|
{
|
||||||
auto size = getWindow().getSize();
|
auto size = getWindow().getSize();
|
||||||
renderer->getRenderer()->setViewport({size.x, size.y});
|
renderer->setViewport(size.x, size.y);
|
||||||
|
|
||||||
ViewCamera viewCam;
|
ViewCamera viewCam;
|
||||||
if( engine->state.currentCutscene != nullptr && engine->state.cutsceneStartTime >= 0.f )
|
if( engine->state.currentCutscene != nullptr && engine->state.cutsceneStartTime >= 0.f )
|
||||||
|
Loading…
Reference in New Issue
Block a user