[Add] Procedural cloud rendering.
Adds a skydome with two scrolling layers of procedurally generated clouds to create an interestingish environment.
This commit is contained in:
parent
8c701d39b7
commit
a1c214caec
29
assets/shaders/cloud.frag
Normal file
29
assets/shaders/cloud.frag
Normal file
@ -0,0 +1,29 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
in vec3 TexCoords;
|
||||
|
||||
uniform sampler2D u_CloudTexture;
|
||||
uniform vec3 u_LightDir;
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
void main() {
|
||||
/* Convert 3D texture coords to 2D using spherical mapping. */
|
||||
float u = atan(TexCoords.z, TexCoords.x) / (2.0 * PI) + 0.5;
|
||||
float v = asin(TexCoords.y) / PI + 0.5;
|
||||
|
||||
/* Sample noise value from cloud texture. */
|
||||
float noise = texture(u_CloudTexture, vec2(u, v)).r;
|
||||
|
||||
/* Create soft-edged clouds from the noise. */
|
||||
float cloudAlpha = smoothstep(0.5, 0.8, noise);
|
||||
|
||||
/* Some simple lighting. */
|
||||
vec3 normal = normalize(TexCoords);
|
||||
float diffuse = max(dot(normal, -u_LightDir), 0.0);
|
||||
vec3 cloudColor = vec3(1.0) * (diffuse * 0.5 + 0.5); /* Add some ambient light. */
|
||||
|
||||
/* Cloud colour is white. it's transparency is determined by alpha. */
|
||||
FragColor = vec4(cloudColor, cloudAlpha);
|
||||
}
|
||||
21
assets/shaders/cloud.vert
Normal file
21
assets/shaders/cloud.vert
Normal file
@ -0,0 +1,21 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
|
||||
out vec3 TexCoords;
|
||||
|
||||
uniform float u_Time;
|
||||
uniform float u_ScrollSpeed;
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
|
||||
void main() {
|
||||
TexCoords = aPos;
|
||||
|
||||
/* Rotate texture coords around Y axis for scrolling. */
|
||||
float angle = u_Time * u_ScrollSpeed;
|
||||
mat3 rot = mat3(cos(angle), 0, sin(angle), 0, 1, 0, -sin(angle), 0, cos(angle));
|
||||
TexCoords = rot * TexCoords;
|
||||
|
||||
gl_Position = projection * view * model * vec4(aPos, 1.0);
|
||||
}
|
||||
17
assets/shaders/sky.frag
Normal file
17
assets/shaders/sky.frag
Normal file
@ -0,0 +1,17 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
in vec3 WorldPos;
|
||||
|
||||
void main() {
|
||||
/* Define colours for the top and bottom of the sky. */
|
||||
vec3 zenithColor = vec3(0.3, 0.5, 0.8);
|
||||
vec3 horizonColor = vec3(0.7, 0.85, 1.0);
|
||||
|
||||
float blendFactor = (normalize(WorldPos).y + 1.0) / 2.0;
|
||||
|
||||
/* Blend the two colours. */
|
||||
vec3 finalColor = mix(horizonColor, zenithColor, blendFactor);
|
||||
|
||||
FragColor = vec4(finalColor, 1.0);
|
||||
}
|
||||
17
assets/shaders/sky.vert
Normal file
17
assets/shaders/sky.vert
Normal file
@ -0,0 +1,17 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
|
||||
out vec3 WorldPos;
|
||||
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
|
||||
void main() {
|
||||
WorldPos = aPos;
|
||||
|
||||
/* Force depth value to 1.0 after perspective division
|
||||
* to ensure the skybox is always rendered behind everythig else.
|
||||
*/
|
||||
vec4 pos = projection * view * vec4(WorldPos, 1.0);
|
||||
gl_Position = pos.xyww;
|
||||
}
|
||||
@ -1,6 +1,9 @@
|
||||
#include <GL/glew.h>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include "bettola/noise/fast_noise_lite.h"
|
||||
#include "game/player.h"
|
||||
#include "game/world.h"
|
||||
#include "graphics/camera.h"
|
||||
@ -22,7 +25,9 @@
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
Renderer::Renderer(void) : _vao(0), _vbo(0), _ground_vao(0), _ground_vbo(0) {}
|
||||
|
||||
Renderer::Renderer(void) : _vao(0), _vbo(0), _sky_vao(0), _sky_vbo(0), _sky_ebo(0),
|
||||
_cloud_vao(0), _cloud_vbo(0), _cloud_texture(0), _sky_indices_count(0) {}
|
||||
|
||||
Renderer::~Renderer(void) {
|
||||
if(_vao != 0) {
|
||||
@ -31,11 +36,20 @@ Renderer::~Renderer(void) {
|
||||
if(_vbo != 0) {
|
||||
glDeleteBuffers(1, &_vbo);
|
||||
}
|
||||
if(_ground_vao != 0) {
|
||||
glDeleteVertexArrays(1, &_ground_vao);
|
||||
if(_sky_vao != 0) {
|
||||
glDeleteVertexArrays(1, &_sky_vao);
|
||||
}
|
||||
if(_ground_vbo != 0) {
|
||||
glDeleteBuffers(1, &_ground_vbo);
|
||||
if(_sky_vbo != 0) {
|
||||
glDeleteBuffers(1, &_sky_vbo);
|
||||
}
|
||||
if(_sky_ebo != 0) {
|
||||
glDeleteBuffers(1, &_sky_ebo);
|
||||
}
|
||||
if(_cloud_texture != 0) {
|
||||
glDeleteTextures(1, &_cloud_texture);
|
||||
}
|
||||
if(_cloud_vao != 0) {
|
||||
glDeleteVertexArrays(1, &_cloud_vao);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,6 +65,23 @@ bool Renderer::init(int screen_width, int screen_height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!_sky_shader.load_from_files("assets/shaders/sky.vert",
|
||||
"assets/shaders/sky.frag")) {
|
||||
fprintf(stderr, "Failed to load sky shaders\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!_cloud_shader.load_from_files("assets/shaders/cloud.vert",
|
||||
"assets/shaders/cloud.frag")) {
|
||||
fprintf(stderr, "Failed to load cloud shaders\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!_init_textures()) {
|
||||
fprintf(stderr, "Failed to init textures\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Definitive, correct 3D cube vertices
|
||||
float vertices[] = {
|
||||
/* Positions. */ /* Normals. */
|
||||
@ -107,26 +138,57 @@ bool Renderer::init(int screen_width, int screen_height) {
|
||||
glEnableVertexAttribArray(0);
|
||||
/* Normal attribute. */
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float)));
|
||||
glEnableVertexAttribArray(1);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
/* Setup ground plane. */
|
||||
float ground_vertices[] = {
|
||||
/* Positions. */
|
||||
500.0f, -0.5f, 500.0f,
|
||||
-500.0f, -0.5f, 500.0f,
|
||||
-500.0f, -0.5f, -500.0f,
|
||||
/* Sky Sphere Generation. */
|
||||
std::vector<float> sky_vertices;
|
||||
std::vector<unsigned int> sky_indices;
|
||||
const int segments = 32;
|
||||
const int rings = 32;
|
||||
|
||||
500.0f, -0.5f, 500.0f,
|
||||
-500.0f, -0.5f, -500.0f,
|
||||
500.0f, -0.5f, -500.0f,
|
||||
};
|
||||
for(int i = 0; i <= rings; i++) {
|
||||
float phi = i * M_PI / rings;
|
||||
for(int j = 0; j <= segments; j++) {
|
||||
float theta = j * 2.0f * M_PI / segments;
|
||||
float x = cos(theta) * sin(phi);
|
||||
float y = cos(phi);
|
||||
float z = sin(theta) * sin(phi);
|
||||
sky_vertices.push_back(x);
|
||||
sky_vertices.push_back(y);
|
||||
sky_vertices.push_back(z);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < rings; i++) {
|
||||
for(int j = 0; j < segments; j++) {
|
||||
int first = (i * (segments + 1)) + j;
|
||||
int second = first + segments + 1;
|
||||
sky_indices.push_back(first);
|
||||
sky_indices.push_back(second);
|
||||
sky_indices.push_back(first+1);
|
||||
|
||||
sky_indices.push_back(second);
|
||||
sky_indices.push_back(second+1);
|
||||
sky_indices.push_back(first+1);
|
||||
}
|
||||
}
|
||||
_sky_indices_count = sky_indices.size();
|
||||
|
||||
glGenVertexArrays(1, &_sky_vao);
|
||||
glGenBuffers(1, &_sky_vbo);
|
||||
glGenBuffers(1, &_sky_ebo);
|
||||
|
||||
glBindVertexArray(_sky_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _sky_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sky_vertices.size()*sizeof(float),
|
||||
sky_vertices.data(), GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _sky_ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sky_indices.size()*sizeof(unsigned int),
|
||||
sky_indices.data(), GL_STATIC_DRAW);
|
||||
|
||||
glGenVertexArrays(1, &_ground_vao);
|
||||
glGenBuffers(1, &_ground_vbo);
|
||||
glBindVertexArray(_ground_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _ground_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(ground_vertices), ground_vertices, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
@ -140,6 +202,107 @@ bool Renderer::init(int screen_width, int screen_height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Renderer::_init_textures(void) {
|
||||
_cloud_texture = _generate_cloud_texture();
|
||||
if(_cloud_texture == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int Renderer::_generate_cloud_texture(void) {
|
||||
const int width = 512;
|
||||
const int height = 512;
|
||||
std::vector<float> buffer(width*height);
|
||||
|
||||
FastNoiseLite noise;
|
||||
noise.SetNoiseType(FastNoiseLite::NoiseType_Perlin);
|
||||
|
||||
noise.SetFractalType(FastNoiseLite::FractalType_FBm);
|
||||
noise.SetFractalOctaves(4);
|
||||
noise.SetFractalLacunarity(2.0f);
|
||||
noise.SetFractalGain(0.5f);
|
||||
noise.SetFrequency(0.05f);
|
||||
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
float noise_val = noise.GetNoise((float)x, (float)y);
|
||||
buffer[y*width+x] = (noise_val + 1.0f) / 2.0f;
|
||||
}
|
||||
}
|
||||
unsigned int texture_id;
|
||||
glGenTextures(1, &texture_id);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_FLOAT, buffer.data());
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
return texture_id;
|
||||
}
|
||||
|
||||
void Renderer::_render_sky(const Camera& camera) {
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
_sky_shader.use();
|
||||
|
||||
/* Remove translation from the view matrix so the skybox follows the camera. */
|
||||
BettolaMath::Mat4 view = camera.get_view_matrix();
|
||||
view.elements[12] = 0;
|
||||
view.elements[13] = 0;
|
||||
view.elements[14] = 0;
|
||||
|
||||
_sky_shader.set_mat4("view", view);
|
||||
_sky_shader.set_mat4("projection", _projection);
|
||||
|
||||
glBindVertexArray(_sky_vao);
|
||||
glDrawElements(GL_TRIANGLES, _sky_indices_count, GL_UNSIGNED_INT, 0);
|
||||
glBindVertexArray(0);
|
||||
glDepthFunc(GL_LESS); /* Put depth function back to default. */
|
||||
}
|
||||
|
||||
void Renderer::_render_clouds(const Camera& camera) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
_cloud_shader.use();
|
||||
|
||||
/* Remove translation from the view matrix so clouds feel infintely far away. */
|
||||
BettolaMath::Mat4 cloud_view = camera.get_view_matrix();
|
||||
cloud_view.elements[12] = 0;
|
||||
cloud_view.elements[13] = 0;
|
||||
cloud_view.elements[14] = 0;
|
||||
|
||||
_cloud_shader.set_mat4("projection", _projection);
|
||||
_cloud_shader.set_mat4("view", cloud_view);
|
||||
_cloud_shader.set_vec3("u_LightDir", {-0.5f, -1.0f, -0.5f});
|
||||
|
||||
glBindVertexArray(_sky_vao);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, _cloud_texture);
|
||||
/* Tell the shader to use texture unit 0. */
|
||||
glUniform1i(glGetUniformLocation(_cloud_shader.get_id(), "u_CloudTexture"),0);
|
||||
|
||||
/* Draw first cloud layer. */
|
||||
BettolaMath::Mat4 model1 = BettolaMath::Mat4::scale(1.0f, 2.0f, 1.0f);
|
||||
_cloud_shader.set_mat4("model", model1);
|
||||
_cloud_shader.set_float("u_Time", (float)SDL_GetTicks() / 1000.0f);
|
||||
_cloud_shader.set_float("u_ScrollSpeed", 0.02f);
|
||||
glDrawElements(GL_TRIANGLES, _sky_indices_count, GL_UNSIGNED_INT, 0);
|
||||
|
||||
/* Draw second cloud layer. */
|
||||
BettolaMath::Mat4 model2 = BettolaMath::Mat4::scale(1.2f, 1.2f, 1.2f);
|
||||
model2 = model2.multiply(BettolaMath::Mat4::rotation(30.0f, {0.0f, 1.0f, 0.0f}));
|
||||
_cloud_shader.set_mat4("model", model2);
|
||||
_cloud_shader.set_float("u_ScrollSpeed", 0.03f);
|
||||
glDrawElements(GL_TRIANGLES, _sky_indices_count, GL_UNSIGNED_INT, 0);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void Renderer::render(const Camera& camera, const Player& player,
|
||||
const std::vector<RemotePlayer>& remote_players,
|
||||
const World& world) {
|
||||
@ -147,14 +310,16 @@ void Renderer::render(const Camera& camera, const Player& player,
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Need to clear depth buffer too. */
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
_projection = BettolaMath::Mat4::perspective((45.0f*M_PI)/180.0f, 800.0f/600.0f, 0.1f, 2000.0f);
|
||||
|
||||
_render_sky(camera);
|
||||
_render_clouds(camera);
|
||||
|
||||
_shader.use();
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
BettolaMath::Mat4 view = camera.get_view_matrix();
|
||||
BettolaMath::Mat4 projection = BettolaMath::Mat4::perspective((45.0f * M_PI) / 180.0f, 800.0f/600.0f, 0.1f, 100.0f);
|
||||
|
||||
_shader.set_mat4("view", view);
|
||||
_shader.set_mat4("projection", projection);
|
||||
_shader.set_mat4("view", camera.get_view_matrix());
|
||||
_shader.set_mat4(("projection"), _projection);
|
||||
|
||||
/* Render world. */
|
||||
/* Set lighting uniforms for terrain. */
|
||||
|
||||
@ -19,13 +19,24 @@ public:
|
||||
const World& world);
|
||||
|
||||
private:
|
||||
bool _init_shaders();
|
||||
bool _init_shaders(void);
|
||||
void _render_sky(const Camera& camera);
|
||||
void _render_clouds(const Camera& camera);
|
||||
bool _init_textures(void);
|
||||
unsigned int _generate_cloud_texture(void);
|
||||
|
||||
Shader _shader;
|
||||
Shader _sky_shader;
|
||||
unsigned int _vao;
|
||||
unsigned int _vbo;
|
||||
unsigned int _ground_vao;
|
||||
unsigned int _ground_vbo;
|
||||
unsigned int _sky_vao;
|
||||
unsigned int _sky_vbo;
|
||||
unsigned int _sky_ebo;
|
||||
unsigned int _sky_indices_count;
|
||||
Shader _cloud_shader;
|
||||
unsigned int _cloud_vao;
|
||||
unsigned int _cloud_vbo;
|
||||
unsigned int _cloud_texture;
|
||||
BettolaMath::Mat4 _projection;
|
||||
};
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ public:
|
||||
|
||||
void set_mat4(const std::string& name, const BettolaMath::Mat4& matrix) const;
|
||||
void set_bool(const std::string& name, bool value) const;
|
||||
void set_float(const std::string& name, float value) const;
|
||||
void set_vec3(const std::string& name, const BettolaMath::Vec3& value) const;
|
||||
unsigned int get_id(void) const { return _program_id; }
|
||||
private:
|
||||
|
||||
@ -14,3 +14,7 @@ void Shader::set_vec3(const std::string& name, const BettolaMath::Vec3& value) c
|
||||
void Shader::set_bool(const std::string& name, bool value) const {
|
||||
glUniform1i(glGetUniformLocation(_program_id, name.c_str()), (int)value);
|
||||
}
|
||||
|
||||
void Shader::set_float(const std::string& name, float value) const {
|
||||
glUniform1f(glGetUniformLocation(_program_id, name.c_str()), value);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user