Skip to content

Commit

Permalink
[DynamicTerrain] The tessellated terrain now has colors
Browse files Browse the repository at this point in the history
- They are computed the same way the static terrain's are, but with a compute shader that's executed after the noise one
  • Loading branch information
Razakhel committed Aug 19, 2023
1 parent 680e8f5 commit 086e675
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 40 deletions.
8 changes: 6 additions & 2 deletions include/Midgard/DynamicTerrain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ class DynamicTerrain final : public Terrain {
explicit DynamicTerrain(Raz::Entity& entity);
DynamicTerrain(Raz::Entity& entity, unsigned int width, unsigned int depth, float heightFactor, float flatness, float minTessLevel = 12.f);

const Raz::Texture2DPtr& getNoiseMap() const noexcept { return m_noiseMap; }
const Raz::Texture2D& getNoiseMap() const noexcept { return *m_noiseMap; }
const Raz::Texture2D& getColorMap() const noexcept { return *m_colorMap; }

void setMinTessellationLevel(float minTessLevel) { setParameters(minTessLevel, m_heightFactor, m_flatness); }
void setParameters(float heightFactor, float flatness) override { setParameters(m_minTessLevel, heightFactor, flatness); }
Expand All @@ -32,13 +33,16 @@ class DynamicTerrain final : public Terrain {
/// \param flatness Flatness of the terrain.
/// \param minTessLevel Minimal tessellation level to render the terrain with.
void generate(unsigned int width, unsigned int depth, float heightFactor, float flatness, float minTessLevel);
const Raz::Texture2DPtr& computeNoiseMap(float factor);
const Raz::Texture2D& computeNoiseMap(float factor);
const Raz::Texture2D& computeColorMap();

private:
float m_minTessLevel {};

Raz::ComputeShaderProgram m_noiseProgram {};
Raz::ComputeShaderProgram m_colorProgram {};
Raz::Texture2DPtr m_noiseMap {};
Raz::Texture2DPtr m_colorMap {};
};

#endif // MIDGARD_DYNAMICTERRAIN_HPP
16 changes: 10 additions & 6 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,22 +191,24 @@ int main() {
overlay.addSeparator();

#if !defined(USE_OPENGL_ES)
Raz::OverlayTexture& dynamicNoiseTexture = overlay.addTexture(*dynamicTerrain.getNoiseMap(), 150, 150);
Raz::OverlayTexture& dynamicNoiseTexture = overlay.addTexture(dynamicTerrain.getNoiseMap(), 150, 150);
Raz::OverlayTexture& dynamicColorTexture = overlay.addTexture(dynamicTerrain.getColorMap(), 150, 150);
#endif

Raz::Texture2D colorTexture(colorMap, false);
Raz::Texture2D normalTexture(normalMap, false);
Raz::Texture2D slopeTexture(slopeMap, false);

Raz::OverlayTexture& staticColorTexture = overlay.addTexture(colorTexture, 150, 150);
Raz::OverlayTexture& staticNormalTexture = overlay.addTexture(normalTexture, 150, 150);
Raz::OverlayTexture& staticSlopeTexture = overlay.addTexture(slopeTexture, 150, 150);
[[maybe_unused]] Raz::OverlayTexture& staticColorTexture = overlay.addTexture(colorTexture, 150, 150);
[[maybe_unused]] Raz::OverlayTexture& staticNormalTexture = overlay.addTexture(normalTexture, 150, 150);
[[maybe_unused]] Raz::OverlayTexture& staticSlopeTexture = overlay.addTexture(slopeTexture, 150, 150);

overlay.addSeparator();

#if !defined(USE_OPENGL_ES)
Raz::OverlaySlider& dynamicNoiseMapFactorSlider = overlay.addSlider("Noise map factor", [&dynamicTerrain] (float value) {
dynamicTerrain.computeNoiseMap(value);
dynamicTerrain.computeColorMap();
}, 0.001f, 0.1f, 0.01f);

Raz::OverlaySlider& dynamicMinTessLevelSlider = overlay.addSlider("Min tess. level", [&dynamicTerrain] (float value) {
Expand All @@ -222,13 +224,13 @@ int main() {
}, 1.f, 10.f, 3.f);
#endif

Raz::OverlaySlider& staticHeightFactorSlider = overlay.addSlider("Height factor", [&staticTerrain, &normalTexture, &slopeTexture] (float value) {
[[maybe_unused]] Raz::OverlaySlider& staticHeightFactorSlider = overlay.addSlider("Height factor", [&staticTerrain, &normalTexture, &slopeTexture] (float value) {
staticTerrain.setHeightFactor(value);
normalTexture.load(staticTerrain.computeNormalMap());
slopeTexture.load(staticTerrain.computeSlopeMap());
}, 0.001f, 50.f, 30.f);

Raz::OverlaySlider& staticFlatnessSlider = overlay.addSlider("Flatness", [&staticTerrain, &normalTexture, &slopeTexture] (float value) {
[[maybe_unused]] Raz::OverlaySlider& staticFlatnessSlider = overlay.addSlider("Flatness", [&staticTerrain, &normalTexture, &slopeTexture] (float value) {
staticTerrain.setFlatness(value);
normalTexture.load(staticTerrain.computeNormalMap());
slopeTexture.load(staticTerrain.computeSlopeMap());
Expand All @@ -244,6 +246,7 @@ int main() {
overlay.addCheckbox("Dynamic terrain", [&] () noexcept {
dynamicTerrainEntity.enable();
dynamicNoiseTexture.enable();
dynamicColorTexture.enable();
dynamicNoiseMapFactorSlider.enable();
dynamicMinTessLevelSlider.enable();
dynamicHeightFactorSlider.enable();
Expand All @@ -265,6 +268,7 @@ int main() {

dynamicTerrainEntity.disable();
dynamicNoiseTexture.disable();
dynamicColorTexture.disable();
dynamicNoiseMapFactorSlider.disable();
dynamicMinTessLevelSlider.disable();
dynamicHeightFactorSlider.disable();
Expand Down
2 changes: 1 addition & 1 deletion shaders/terrain.tesc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void main() {
float tessLevelFactor = (1.0 / distance(cameraPos, patchCentroid)) * 512.0;

// Outer levels get a higher factor in order to attempt filling gaps between patches
// TODO: A better way must be found, like varying the level according the edges mid points
// TODO: A better way must be found, like making the outer level vary according to the edges' mid points
// See: https://www.khronos.org/opengl/wiki/Tessellation#Patch_interface_and_continuity
gl_TessLevelOuter[0] = uniTessLevel * (tessLevelFactor * 4.0);
gl_TessLevelOuter[1] = uniTessLevel * (tessLevelFactor * 4.0);
Expand Down
30 changes: 15 additions & 15 deletions shaders/terrain.tese
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ struct MeshInfo {

in MeshInfo tessMeshInfo[];

uniform sampler2D uniNoiseMap;
uniform sampler2D uniHeightmap;
uniform uvec2 uniTerrainSize;
uniform float uniFlatness = 3.0;
uniform float uniHeightFactor = 30.0;
Expand All @@ -28,10 +28,10 @@ vec3 computeNormalDifferences(vec2 vertUV) {
float uStride = 1.0 / float(uniTerrainSize.x);
float vStride = 1.0 / float(uniTerrainSize.y);

float topHeight = pow(texture(uniNoiseMap, vertUV + vec2( 0.0, -vStride)).r, uniFlatness) * uniHeightFactor;
float leftHeight = pow(texture(uniNoiseMap, vertUV + vec2(-uStride, 0.0)).r, uniFlatness) * uniHeightFactor;
float rightHeight = pow(texture(uniNoiseMap, vertUV + vec2( uStride, 0.0)).r, uniFlatness) * uniHeightFactor;
float botHeight = pow(texture(uniNoiseMap, vertUV + vec2( 0.0, vStride)).r, uniFlatness) * uniHeightFactor;
float topHeight = pow(texture(uniHeightmap, vertUV + vec2( 0.0, -vStride)).r, uniFlatness) * uniHeightFactor;
float leftHeight = pow(texture(uniHeightmap, vertUV + vec2(-uStride, 0.0)).r, uniFlatness) * uniHeightFactor;
float rightHeight = pow(texture(uniHeightmap, vertUV + vec2( uStride, 0.0)).r, uniFlatness) * uniHeightFactor;
float botHeight = pow(texture(uniHeightmap, vertUV + vec2( 0.0, vStride)).r, uniFlatness) * uniHeightFactor;

// Replace y by 2.0-2.5 to get the same result as the cross method
return normalize(vec3(leftHeight - rightHeight, 1.0, topHeight - botHeight));
Expand All @@ -46,15 +46,15 @@ vec3 computeNormalCross(vec3 vertPos, vec2 vertUV) {
vec3 bottomRightPos = vertPos + vec3( 1.0, 0.0, -1.0);
vec3 topRightPos = vertPos + vec3( 1.0, 0.0, 1.0);

float bottomLeftNoise = texture(uniNoiseMap, vertUV + vec2(-uStride, -vStride)).r;
float topLeftNoise = texture(uniNoiseMap, vertUV + vec2(-uStride, vStride)).r;
float bottomRightNoise = texture(uniNoiseMap, vertUV + vec2( uStride, -vStride)).r;
float topRightNoise = texture(uniNoiseMap, vertUV + vec2( uStride, vStride)).r;
float bottomLeftHeight = texture(uniHeightmap, vertUV + vec2(-uStride, -vStride)).r;
float topLeftHeight = texture(uniHeightmap, vertUV + vec2(-uStride, vStride)).r;
float bottomRightHeight = texture(uniHeightmap, vertUV + vec2( uStride, -vStride)).r;
float topRightHeight = texture(uniHeightmap, vertUV + vec2( uStride, vStride)).r;

bottomLeftPos.y += pow(bottomLeftNoise, uniFlatness) * uniHeightFactor;
topLeftPos.y += pow(topLeftNoise, uniFlatness) * uniHeightFactor;
bottomRightPos.y += pow(bottomRightNoise, uniFlatness) * uniHeightFactor;
topRightPos.y += pow(topRightNoise, uniFlatness) * uniHeightFactor;
bottomLeftPos.y += pow(bottomLeftHeight, uniFlatness) * uniHeightFactor;
topLeftPos.y += pow(topLeftHeight, uniFlatness) * uniHeightFactor;
bottomRightPos.y += pow(bottomRightHeight, uniFlatness) * uniHeightFactor;
topRightPos.y += pow(topRightHeight, uniFlatness) * uniHeightFactor;

vec3 bottomLeftDir = bottomLeftPos - vertPos;
vec3 topLeftDir = topLeftPos - vertPos;
Expand Down Expand Up @@ -86,8 +86,8 @@ void main() {
vec2 vertUV1 = mix(tessMeshInfo[2].vertTexcoords, tessMeshInfo[3].vertTexcoords, gl_TessCoord.x);
vec2 vertUV = mix(vertUV0, vertUV1, gl_TessCoord.y);

float midNoise = texture(uniNoiseMap, vertUV).r;
vertPos.y += pow(midNoise, uniFlatness) * uniHeightFactor;
float midHeight = texture(uniHeightmap, vertUV).r;
vertPos.y += pow(midHeight, uniFlatness) * uniHeightFactor;

vec3 normal = computeNormalDifferences(vertUV);
//vec3 normal = computeNormalCross(vertPos, vertUV);
Expand Down
22 changes: 22 additions & 0 deletions shaders/terrain_color.comp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
layout(local_size_x = 1, local_size_y = 1, local_size_x = 1) in;

const vec3 waterColor = vec3(0.0, 0.0, 1.0);
const vec3 grassColor = vec3(0.24, 0.49, 0.0);
const vec3 groundColor = vec3(0.62, 0.43, 0.37);
const vec3 rockColor = vec3(0.5, 0.5, 0.5);
const vec3 snowColor = vec3(1.0, 1.0, 1.0);

layout(r16f, binding = 0) uniform readonly image2D uniHeightmap;
layout(rgba8, binding = 1) uniform writeonly image2D uniColorMap;

void main() {
ivec2 pixelCoords = ivec2(gl_GlobalInvocationID.xy);

float height = imageLoad(uniHeightmap, pixelCoords).r;
vec3 color = (height < 0.33 ? mix(waterColor, grassColor, height * 3.0)
: (height < 0.5 ? mix(grassColor, groundColor, (height - 0.33) * 5.75)
: (height < 0.66 ? mix(groundColor, rockColor, (height - 0.5) * 6.0)
: mix(rockColor, snowColor, (height - 0.66) * 3.0))));

imageStore(uniColorMap, pixelCoords, vec4(color, 1.0));
}
58 changes: 42 additions & 16 deletions src/Midgard/DynamicTerrain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ constexpr std::string_view noiseCompSource = {
#include "perlin_noise_2d.comp.embed"
};

constexpr std::string_view colorCompSource = {
#include "terrain_color.comp.embed"
};

inline void checkParameters(float& minTessLevel) {
if (minTessLevel <= 0.f) {
Raz::Logger::warn("[DynamicTerrain] The minimal tessellation level can't be 0 or negative; remapping to +epsilon.");
Expand All @@ -35,23 +39,39 @@ DynamicTerrain::DynamicTerrain(Raz::Entity& entity) : Terrain(entity) {
terrainProgram.setTessellationEvaluationShader(Raz::TessellationEvaluationShader::loadFromSource(tessEvalSource));
terrainProgram.link();

m_noiseMap = Raz::Texture2D::create(1024, 1024, Raz::TextureColorspace::GRAY, Raz::TextureDataType::FLOAT16);
m_colorMap = Raz::Texture2D::create(1024, 1024, Raz::TextureColorspace::RGBA, Raz::TextureDataType::BYTE);

#if !defined(USE_OPENGL_ES)
if (Raz::Renderer::checkVersion(4, 3)) {
Raz::Renderer::setLabel(Raz::RenderObjectType::TEXTURE, m_noiseMap->getIndex(), "Noise map");
Raz::Renderer::setLabel(Raz::RenderObjectType::TEXTURE, m_colorMap->getIndex(), "Color map");
}
#endif

m_noiseProgram.setShader(Raz::ComputeShader::loadFromSource(noiseCompSource));
m_noiseProgram.setAttribute(8, "uniOctaveCount");
m_noiseProgram.sendAttributes();
m_noiseProgram.use();
m_noiseProgram.sendUniform("uniOctaveCount", 8);

m_noiseMap = Raz::Texture2D::create(1024, 1024, Raz::TextureColorspace::GRAY, Raz::TextureDataType::FLOAT16);
Raz::Renderer::bindImageTexture(0, m_noiseMap->getIndex(), 0, false, 0, Raz::ImageAccess::WRITE, Raz::ImageInternalFormat::R16F);

terrainProgram.setTexture(m_noiseMap, "uniNoiseMap");
m_colorProgram.setShader(Raz::ComputeShader::loadFromSource(colorCompSource));
m_colorProgram.use();
Raz::Renderer::bindImageTexture(0, m_noiseMap->getIndex(), 0, false, 0, Raz::ImageAccess::READ, Raz::ImageInternalFormat::R16F);
Raz::Renderer::bindImageTexture(1, m_colorMap->getIndex(), 0, false, 0, Raz::ImageAccess::WRITE, Raz::ImageInternalFormat::RGBA8);

terrainProgram.setTexture(m_noiseMap, "uniHeightmap");
terrainProgram.setTexture(m_colorMap, Raz::MaterialTexture::BaseColor);

computeNoiseMap(0.01f);
computeColorMap();
}

DynamicTerrain::DynamicTerrain(Raz::Entity& entity, unsigned int width, unsigned int depth,
float heightFactor, float flatness, float minTessLevel) : DynamicTerrain(entity) {
Raz::RenderShaderProgram& program = entity.getComponent<Raz::MeshRenderer>().getMaterials().front().getProgram();
program.use();
program.sendUniform("uniTerrainSize", Raz::Vec2u(width, depth));
Raz::RenderShaderProgram& terrainProgram = entity.getComponent<Raz::MeshRenderer>().getMaterials().front().getProgram();
terrainProgram.setAttribute(Raz::Vec2u(width, depth), "uniTerrainSize");
terrainProgram.sendAttributes();

DynamicTerrain::generate(width, depth, heightFactor, flatness, minTessLevel);
}
Expand All @@ -62,11 +82,11 @@ void DynamicTerrain::setParameters(float minTessLevel, float heightFactor, float
::checkParameters(minTessLevel);
m_minTessLevel = minTessLevel;

Raz::RenderShaderProgram& program = m_entity.getComponent<Raz::MeshRenderer>().getMaterials().front().getProgram();
program.use();
program.sendUniform("uniTessLevel", m_minTessLevel);
program.sendUniform("uniHeightFactor", m_heightFactor);
program.sendUniform("uniFlatness", m_flatness);
Raz::RenderShaderProgram& terrainProgram = m_entity.getComponent<Raz::MeshRenderer>().getMaterials().front().getProgram();
terrainProgram.setAttribute(m_minTessLevel, "uniTessLevel");
terrainProgram.setAttribute(m_heightFactor, "uniHeightFactor");
terrainProgram.setAttribute(m_flatness, "uniFlatness");
terrainProgram.sendAttributes();
}

void DynamicTerrain::generate(unsigned int width, unsigned int depth, float heightFactor, float flatness, float minTessLevel) {
Expand Down Expand Up @@ -126,10 +146,16 @@ void DynamicTerrain::generate(unsigned int width, unsigned int depth, float heig
setParameters(minTessLevel, heightFactor, flatness);
}

const Raz::Texture2DPtr& DynamicTerrain::computeNoiseMap(float factor) {
m_noiseProgram.use();
m_noiseProgram.sendUniform("uniNoiseFactor", factor);
const Raz::Texture2D& DynamicTerrain::computeNoiseMap(float factor) {
m_noiseProgram.setAttribute(factor, "uniNoiseFactor");
m_noiseProgram.sendAttributes();
m_noiseProgram.execute(1024, 1024);

return m_noiseMap;
return *m_noiseMap;
}

const Raz::Texture2D& DynamicTerrain::computeColorMap() {
m_colorProgram.execute(1024, 1024);

return *m_colorMap;
}

0 comments on commit 086e675

Please sign in to comment.