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 texture
hud
EmaMaker 2023-09-17 10:40:55 +02:00
parent 6670f3b41c
commit 1c0ee1315f
6 changed files with 266 additions and 11 deletions

View File

@ -11,8 +11,9 @@
namespace renderer{
typedef oneapi::tbb::concurrent_unordered_set<Chunk::Chunk*> RenderSet;
void init();
void render();
void init(GLFWwindow* window);
void render(GLFWwindow* window);
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void destroy();
Shader* getRenderShader();
RenderSet& getChunksToRender();

143
intervalmap.hpp Normal file
View File

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

10
shaders/shader-quad.fs Normal file
View File

@ -0,0 +1,10 @@
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D renderTex;
void main(){
FragColor = texture(renderTex, TexCoord);
}

12
shaders/shader-quad.vs Normal file
View File

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

View File

@ -57,7 +57,6 @@ int main()
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetCursorPosCallback(window, mouse_callback);
glEnable(GL_DEPTH_TEST);
//glEnable(GL_FRAMEBUFFER_SRGB); //gamma correction done in fragment shader
//glEnable(GL_CULL_FACE); //GL_BACK GL_CCW by default
@ -70,7 +69,7 @@ int main()
}
SpaceFilling::initLUT();
chunkmanager::init();
renderer::init();
renderer::init(window);
while (!glfwWindowShouldClose(window))
{
@ -100,7 +99,7 @@ int main()
if(glfwGetTime() - lastBlockPick > 0.1) blockpick = false;
// Render pass
renderer::render();
renderer::render(window);
// Swap buffers to avoid tearing
glfwSwapBuffers(window);
@ -122,6 +121,7 @@ void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
glViewport(0, 0, width, height);
theCamera.viewPortCallBack(window, width, height);
renderer::framebuffer_size_callback(window, width, height);
}
void mouse_callback(GLFWwindow *window, double xpos, double ypos)

View File

@ -13,18 +13,73 @@ namespace renderer{
oneapi::tbb::concurrent_vector<Chunk::Chunk*> render_todelete;
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*> MeshDataQueue;
Shader* theShader;
Shader* theShader, *quadShader;
GLuint chunkTexture;
Shader* getRenderShader() { return theShader; }
RenderSet& getChunksToRender(){ return chunks_torender; }
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
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
constexpr int layerCount = 5;
glGenTextures(1, &chunkTexture);
@ -49,7 +104,18 @@ namespace renderer{
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};
glm::vec4 frustumPlanes[6];
theCamera.getFrustumPlanes(frustumPlanes, true);
@ -65,9 +131,6 @@ namespace renderer{
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){
float dist = glm::distance(c->getPosition(), cameraChunkPos);
if(dist <= static_cast<float>(RENDER_DISTANCE)){
@ -139,6 +202,32 @@ namespace renderer{
chunkmanager::getDeleteVector().push(c);
}
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(){