renderer: render scene to a texture
And then render the texture onto a quad that fills the screen The screen-filled quad upon which the texture is rendered must never be rendered in wireframe. Rendering in wireframe only makes sense when rendering the world on the texturehud
parent
6670f3b41c
commit
1c0ee1315f
|
@ -11,8 +11,9 @@
|
||||||
namespace renderer{
|
namespace renderer{
|
||||||
typedef oneapi::tbb::concurrent_unordered_set<Chunk::Chunk*> RenderSet;
|
typedef oneapi::tbb::concurrent_unordered_set<Chunk::Chunk*> RenderSet;
|
||||||
|
|
||||||
void init();
|
void init(GLFWwindow* window);
|
||||||
void render();
|
void render(GLFWwindow* window);
|
||||||
|
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
|
||||||
void destroy();
|
void destroy();
|
||||||
Shader* getRenderShader();
|
Shader* getRenderShader();
|
||||||
RenderSet& getChunksToRender();
|
RenderSet& getChunksToRender();
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
#ifndef INTERVALMAP_H
|
||||||
|
#define INTERVALMAP_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <iterator> //std::prev
|
||||||
|
#include <limits> // std::numeric_limits
|
||||||
|
#include <memory> //std::shared_ptr
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
template <typename K, typename V>
|
||||||
|
class IntervalMap
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
~IntervalMap(){
|
||||||
|
treemap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(K start, K end, V value)
|
||||||
|
{
|
||||||
|
if (start >= end)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The entry just before the end index
|
||||||
|
auto tmp = treemap.upper_bound(end);
|
||||||
|
auto end_prev_entry = tmp == treemap.end() ? tmp : --tmp;
|
||||||
|
auto added_end = treemap.end();
|
||||||
|
|
||||||
|
// If it doesn't exist (empty map)
|
||||||
|
if(end_prev_entry == treemap.end()){
|
||||||
|
V v{};
|
||||||
|
added_end = treemap.insert_or_assign(treemap.begin(), end, v);
|
||||||
|
}
|
||||||
|
// Or if it has value different from the insertion
|
||||||
|
else if(end_prev_entry->second != value)
|
||||||
|
// Add it back at the end
|
||||||
|
added_end = treemap.insert_or_assign(end_prev_entry, end, end_prev_entry->second);
|
||||||
|
|
||||||
|
// The entry just before the start index
|
||||||
|
tmp = treemap.upper_bound(start);
|
||||||
|
auto start_prev_entry = tmp == treemap.end() ? tmp : --tmp;
|
||||||
|
auto added_start = treemap.end();
|
||||||
|
// If it has value different from the insertion
|
||||||
|
if(start_prev_entry == treemap.end() || start_prev_entry->second != value)
|
||||||
|
// Add the start node of the insertion
|
||||||
|
added_start = treemap.insert_or_assign(start_prev_entry, start, value);
|
||||||
|
|
||||||
|
// Delete everything else inside
|
||||||
|
// map.erase(start, end) deletes every node with key in the range [start, end)
|
||||||
|
|
||||||
|
// The key to start deleting from is the key after the start node we added
|
||||||
|
// (We don't want to delete a node we just added)
|
||||||
|
auto del_start = added_start == treemap.end() ? std::next(start_prev_entry) : std::next(added_start);
|
||||||
|
auto del_end = added_end == treemap.end() ? end_prev_entry : added_end;
|
||||||
|
auto del_end_next = std::next(del_end);
|
||||||
|
|
||||||
|
// If the node after the end is of the same type of the end, delete it
|
||||||
|
// We cannot just expand del_end (++del_end) otherwise interval limits get messed up
|
||||||
|
if(del_end != treemap.end() && del_end_next != treemap.end() && del_end->second ==
|
||||||
|
del_end_next->second) treemap.erase(del_end_next);
|
||||||
|
|
||||||
|
// Delete everything in between
|
||||||
|
if(del_start != treemap.end() && (del_end==treemap.end() || del_start->first <
|
||||||
|
del_end->first)) treemap.erase(del_start, del_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(K at)
|
||||||
|
{
|
||||||
|
treemap.erase(at);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto at(K index)
|
||||||
|
{
|
||||||
|
const auto tmp = treemap.lower_bound(index);
|
||||||
|
const auto r = tmp != treemap.begin() && tmp->first!=index ? std::prev(tmp) : tmp;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print()
|
||||||
|
{
|
||||||
|
for (auto i = treemap.begin(); i != treemap.end(); i++)
|
||||||
|
std::cout << i->first << ": " << (int)(i->second) << "\n";
|
||||||
|
if(!treemap.empty()) std::cout << "end key: " << std::prev(treemap.end())->first << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<V[]> toArray(int *length)
|
||||||
|
{
|
||||||
|
if (treemap.empty())
|
||||||
|
{
|
||||||
|
*length = 0;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &end = std::prev(treemap.end());
|
||||||
|
*length = end->first;
|
||||||
|
if(*length == 0) return nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr<V[]> arr(new V[*length]);
|
||||||
|
auto start = treemap.begin();
|
||||||
|
for (auto i = std::next(treemap.begin()); i != treemap.end(); i++)
|
||||||
|
{
|
||||||
|
for (int k = start->first; k < i->first; k++)
|
||||||
|
arr[k] = start->second;
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fromArray(V *arr, int length)
|
||||||
|
{
|
||||||
|
treemap.clear();
|
||||||
|
|
||||||
|
if (length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
V prev = arr[0];
|
||||||
|
unsigned int prev_start = 0;
|
||||||
|
for (unsigned int i = 1; i < length; i++)
|
||||||
|
{
|
||||||
|
if (prev != arr[i])
|
||||||
|
{
|
||||||
|
insert(prev_start, i, prev);
|
||||||
|
prev_start = i;
|
||||||
|
}
|
||||||
|
prev = arr[i];
|
||||||
|
}
|
||||||
|
insert(prev_start, length, prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto begin(){
|
||||||
|
return treemap.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end(){
|
||||||
|
return treemap.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<K, V> treemap{};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,10 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec2 TexCoord;
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
uniform sampler2D renderTex;
|
||||||
|
|
||||||
|
void main(){
|
||||||
|
FragColor = texture(renderTex, TexCoord);
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 aPos;
|
||||||
|
layout (location = 1) in vec2 aTexCoord;
|
||||||
|
|
||||||
|
out vec2 TexCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
TexCoord = aTexCoord;
|
||||||
|
gl_Position = vec4(aPos, 1.0);
|
||||||
|
}
|
|
@ -57,7 +57,6 @@ int main()
|
||||||
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||||
glfwSetCursorPosCallback(window, mouse_callback);
|
glfwSetCursorPosCallback(window, mouse_callback);
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
//glEnable(GL_FRAMEBUFFER_SRGB); //gamma correction done in fragment shader
|
//glEnable(GL_FRAMEBUFFER_SRGB); //gamma correction done in fragment shader
|
||||||
//glEnable(GL_CULL_FACE); //GL_BACK GL_CCW by default
|
//glEnable(GL_CULL_FACE); //GL_BACK GL_CCW by default
|
||||||
|
|
||||||
|
@ -70,7 +69,7 @@ int main()
|
||||||
}
|
}
|
||||||
SpaceFilling::initLUT();
|
SpaceFilling::initLUT();
|
||||||
chunkmanager::init();
|
chunkmanager::init();
|
||||||
renderer::init();
|
renderer::init(window);
|
||||||
|
|
||||||
while (!glfwWindowShouldClose(window))
|
while (!glfwWindowShouldClose(window))
|
||||||
{
|
{
|
||||||
|
@ -100,7 +99,7 @@ int main()
|
||||||
if(glfwGetTime() - lastBlockPick > 0.1) blockpick = false;
|
if(glfwGetTime() - lastBlockPick > 0.1) blockpick = false;
|
||||||
|
|
||||||
// Render pass
|
// Render pass
|
||||||
renderer::render();
|
renderer::render(window);
|
||||||
|
|
||||||
// Swap buffers to avoid tearing
|
// Swap buffers to avoid tearing
|
||||||
glfwSwapBuffers(window);
|
glfwSwapBuffers(window);
|
||||||
|
@ -122,6 +121,7 @@ void framebuffer_size_callback(GLFWwindow *window, int width, int height)
|
||||||
{
|
{
|
||||||
glViewport(0, 0, width, height);
|
glViewport(0, 0, width, height);
|
||||||
theCamera.viewPortCallBack(window, width, height);
|
theCamera.viewPortCallBack(window, width, height);
|
||||||
|
renderer::framebuffer_size_callback(window, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mouse_callback(GLFWwindow *window, double xpos, double ypos)
|
void mouse_callback(GLFWwindow *window, double xpos, double ypos)
|
||||||
|
|
101
src/renderer.cpp
101
src/renderer.cpp
|
@ -13,18 +13,73 @@ namespace renderer{
|
||||||
oneapi::tbb::concurrent_vector<Chunk::Chunk*> render_todelete;
|
oneapi::tbb::concurrent_vector<Chunk::Chunk*> render_todelete;
|
||||||
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*> MeshDataQueue;
|
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*> MeshDataQueue;
|
||||||
|
|
||||||
Shader* theShader;
|
Shader* theShader, *quadShader;
|
||||||
GLuint chunkTexture;
|
GLuint chunkTexture;
|
||||||
|
|
||||||
Shader* getRenderShader() { return theShader; }
|
Shader* getRenderShader() { return theShader; }
|
||||||
RenderSet& getChunksToRender(){ return chunks_torender; }
|
RenderSet& getChunksToRender(){ return chunks_torender; }
|
||||||
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*>& getMeshDataQueue(){ return MeshDataQueue; }
|
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*>& getMeshDataQueue(){ return MeshDataQueue; }
|
||||||
|
|
||||||
|
GLuint renderTexFrameBuffer, renderTex, renderTexDepthBuffer, quadVAO, quadVBO;
|
||||||
|
int screenWidth, screenHeight;
|
||||||
|
|
||||||
void init(){
|
|
||||||
|
void init(GLFWwindow* window){
|
||||||
|
// Setup rendering
|
||||||
|
// We will render the image to a texture, then display the texture on a quad that fills the
|
||||||
|
// entire screen.
|
||||||
|
// This makes it easy to capture screenshots or apply filters to the final image (e.g.
|
||||||
|
// over-impress HUD elements like a crosshair)
|
||||||
|
glfwGetWindowSize(window, &screenWidth, &screenHeight);
|
||||||
|
|
||||||
|
glGenFramebuffers(1, &renderTexFrameBuffer);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, renderTexFrameBuffer);
|
||||||
|
|
||||||
|
// Depth buffer
|
||||||
|
glGenRenderbuffers(1, &renderTexDepthBuffer);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, renderTexDepthBuffer);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, screenWidth, screenHeight); //Support up to
|
||||||
|
//full-hd for now
|
||||||
|
// Attach it to the frame buffer
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
|
||||||
|
renderTexDepthBuffer);
|
||||||
|
// Create texture to render to
|
||||||
|
// The texture we're going to render to
|
||||||
|
glGenTextures(1, &renderTex);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, renderTex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, screenWidth, screenHeight, 0,GL_RGB, GL_UNSIGNED_BYTE, 0); // Support
|
||||||
|
// up to
|
||||||
|
// full-hd
|
||||||
|
// for now
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
// Set the texture as a render attachment for the framebuffer
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTex, 0);
|
||||||
|
|
||||||
|
// Create the quad to render the texture to
|
||||||
|
float vertices[] = {
|
||||||
|
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
|
||||||
|
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
|
||||||
|
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
|
||||||
|
1.0f, 1.0f, 0.0f, 1.0f, 1.0f
|
||||||
|
};
|
||||||
|
glGenBuffers(1, &quadVBO);
|
||||||
|
glGenVertexArrays(1, &quadVAO);
|
||||||
|
glBindVertexArray(quadVAO);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)0);
|
||||||
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)(3*sizeof(float)));
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
// Rendering of the world
|
||||||
// Create Shader
|
// Create Shader
|
||||||
theShader = new Shader{"shaders/shader-texture.gs", "shaders/shader-texture.vs", "shaders/shader-texture.fs"};
|
theShader = new Shader{"shaders/shader-texture.gs", "shaders/shader-texture.vs", "shaders/shader-texture.fs"};
|
||||||
|
quadShader = new Shader{nullptr, "shaders/shader-quad.vs", "shaders/shader-quad.fs"};
|
||||||
|
|
||||||
|
// Block textures
|
||||||
// Create 3d array texture
|
// Create 3d array texture
|
||||||
constexpr int layerCount = 5;
|
constexpr int layerCount = 5;
|
||||||
glGenTextures(1, &chunkTexture);
|
glGenTextures(1, &chunkTexture);
|
||||||
|
@ -49,7 +104,18 @@ namespace renderer{
|
||||||
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_T,GL_REPEAT);
|
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_T,GL_REPEAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void render(){
|
|
||||||
|
void render(GLFWwindow* window){
|
||||||
|
// Bind the frame buffer to render to the texture
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, renderTexFrameBuffer);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
if(wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||||
|
else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
|
|
||||||
|
// Clear the screen
|
||||||
|
glClearColor(0.431f, 0.694f, 1.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
int total{0}, toGpu{0};
|
int total{0}, toGpu{0};
|
||||||
glm::vec4 frustumPlanes[6];
|
glm::vec4 frustumPlanes[6];
|
||||||
theCamera.getFrustumPlanes(frustumPlanes, true);
|
theCamera.getFrustumPlanes(frustumPlanes, true);
|
||||||
|
@ -65,9 +131,6 @@ namespace renderer{
|
||||||
chunkmesher::getMeshDataQueue().push(m);
|
chunkmesher::getMeshDataQueue().push(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
||||||
else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
||||||
|
|
||||||
for(auto& c : chunks_torender){
|
for(auto& c : chunks_torender){
|
||||||
float dist = glm::distance(c->getPosition(), cameraChunkPos);
|
float dist = glm::distance(c->getPosition(), cameraChunkPos);
|
||||||
if(dist <= static_cast<float>(RENDER_DISTANCE)){
|
if(dist <= static_cast<float>(RENDER_DISTANCE)){
|
||||||
|
@ -139,6 +202,32 @@ namespace renderer{
|
||||||
chunkmanager::getDeleteVector().push(c);
|
chunkmanager::getDeleteVector().push(c);
|
||||||
}
|
}
|
||||||
render_todelete.clear();
|
render_todelete.clear();
|
||||||
|
|
||||||
|
|
||||||
|
// Now to render the quad, with the texture on top
|
||||||
|
// Switch to the default frame buffer
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glClearColor(0.431f, 0.694f, 1.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
|
|
||||||
|
glBindVertexArray(quadVAO);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, renderTex);
|
||||||
|
quadShader->use();
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void framebuffer_size_callback(GLFWwindow *window, int width, int height){
|
||||||
|
screenWidth = width;
|
||||||
|
screenHeight = height;
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, renderTex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, screenWidth, screenHeight, 0,GL_RGB, GL_UNSIGNED_BYTE, 0); // Support
|
||||||
|
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, renderTexDepthBuffer);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, screenWidth, screenHeight); //Support up to
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy(){
|
void destroy(){
|
||||||
|
|
Loading…
Reference in New Issue