-
-
Notifications
You must be signed in to change notification settings - Fork 29
Lightning effect
cook-torrance.frag
shader: color = color / (color + vec3(1.0));
. This is obviously not the wanted behavior and will change in the near future; this message will then be removed accordingly.
To be precise, the bloom actually has a threshold of 0.75 to actually have an effect until this tone mapping gets removed. However, even very high blue emissive values on the lightning actually get ignored on thresholding. This does not happen (so much) without the tone mapping, hence this manual change required.
As the time of writing (2023-01-17, commit 656398d), the following files give the result shown in this video:
Using this code, you can perform several actions:
- Add a line with
Page Up
, up to 64; - Remove a line with
Page Down
, down to 1; - Toggle between slow & fast mode with
R
.
#include <RaZ/Application.hpp>
#include <RaZ/Data/Color.hpp>
#include <RaZ/Data/Mesh.hpp>
#include <RaZ/Data/ObjFormat.hpp>
#include <RaZ/Math/Transform.hpp>
#include <RaZ/Render/BloomRenderProcess.hpp>
#include <RaZ/Render/BoxBlurRenderProcess.hpp>
#include <RaZ/Render/Camera.hpp>
#include <RaZ/Render/Light.hpp>
#include <RaZ/Render/MeshRenderer.hpp>
#include <RaZ/Render/RenderSystem.hpp>
#include <RaZ/Render/SsrRenderProcess.hpp>
#include <random>
using namespace Raz::Literals;
constexpr unsigned int textureSize = 512;
int main() {
Raz::Application app;
Raz::World& world = app.addWorld(7);
auto& render = world.addSystem<Raz::RenderSystem>(1280, 720, "RaZ");
if (!Raz::Renderer::checkVersion(4, 0) && !Raz::Renderer::isExtensionSupported("GL_ARB_tessellation_shader")) {
throw std::runtime_error("Error: Tessellation is only available with an OpenGL 4.0 context or above, or with the 'GL_ARB_tessellation_shader' extension; "
"please update your graphics drivers or try on another computer");
}
if (!Raz::Renderer::checkVersion(4, 3) && !Raz::Renderer::isExtensionSupported("GL_ARB_compute_shader")) {
throw std::runtime_error("Error: Compute is only available with an OpenGL 4.3 context or above, or with the 'GL_ARB_compute_shader' extension; "
"please update your graphics drivers or try on another computer");
}
Raz::Window& window = render.getWindow();
window.addKeyCallback(Raz::Keyboard::ESCAPE, [&app] (float) noexcept { app.quit(); });
window.setCloseCallback([&app] () noexcept { app.quit(); });
Raz::Entity& camera = world.addEntityWithComponent<Raz::Transform>(Raz::Vec3f(0.f, 0.f, 5.f));
camera.addComponent<Raz::Camera>(window.getWidth(), window.getHeight());
const auto noiseTexture = Raz::Texture1D::create(textureSize, Raz::TextureColorspace::GRAY, Raz::TextureDataType::FLOAT16);
Raz::Renderer::bindImageTexture(0, noiseTexture->getIndex(), 0, false, 0, Raz::ImageAccess::WRITE, Raz::ImageInternalFormat::R16F);
Raz::ComputeShaderProgram noise(Raz::ComputeShader("perlin_noise_1d.comp"));
noise.setAttribute(0.015f, "uniNoiseFactor");
noise.setAttribute(8, "uniOctaveCount");
noise.sendAttributes();
noise.execute(textureSize);
Raz::Entity& mesh = world.addEntityWithComponent<Raz::Transform>(Raz::Vec3f(0.2f, -1.f, 4.7f), Raz::Quaternionf(90_deg, Raz::Axis::Y), Raz::Vec3f(0.0053f));
mesh.addComponent<Raz::MeshRenderer>(Raz::ObjFormat::load("crytek_sponza.obj").second);
Raz::Entity& lightning = world.addEntityWithComponent<Raz::Transform>();
Raz::Mesh lightningPoints;
lightningPoints.addSubmesh().getVertices() = {
Raz::Vertex{ Raz::Vec3f(-1.f, 0.f, 0.f) },
Raz::Vertex{ Raz::Vec3f(1.f, 0.f, 0.f) },
Raz::Vertex{ Raz::Vec3f(-1.f, 0.f, 10.1f) },
Raz::Vertex{ Raz::Vec3f(1.f, 0.f, 10.1f) }
};
////////////////////////
// Lightning material //
////////////////////////
auto& lightningProgram = lightning.addComponent<Raz::MeshRenderer>(lightningPoints, Raz::RenderMode::PATCH).getMaterials().front().getProgram();
lightningProgram.setAttribute(Raz::ColorPreset::Blue, Raz::MaterialAttribute::BaseColor);
// We're giving a very high blue value, since the bloom's thresholding pass reduces it so much
lightningProgram.setAttribute(Raz::Vec3f(0.1f, 0.1f, 50.f), Raz::MaterialAttribute::Emissive);
Raz::Renderer::setPatchVertexCount(2); // To render points 2 by 2, creating lines
lightningProgram.setTessellationControlShader(Raz::TessellationControlShader::loadFromSource(R"(
layout(vertices = 2) out;
struct MeshInfo {
vec3 vertPosition;
vec2 vertTexcoords;
mat3 vertTBNMatrix;
};
in MeshInfo vertMeshInfo[];
uniform float uniLineCount = 1.0;
uniform float uniPointCount = 64.0;
out MeshInfo tessMeshInfo[];
void main() {
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
tessMeshInfo[gl_InvocationID].vertPosition = vertMeshInfo[gl_InvocationID].vertPosition;
tessMeshInfo[gl_InvocationID].vertTexcoords = vertMeshInfo[gl_InvocationID].vertTexcoords;
tessMeshInfo[gl_InvocationID].vertTBNMatrix = vertMeshInfo[gl_InvocationID].vertTBNMatrix;
if (gl_InvocationID == 0) {
gl_TessLevelOuter[0] = uniLineCount; // How many lines to create
gl_TessLevelOuter[1] = uniPointCount; // How many points to create for each line
}
}
)"));
lightningProgram.setTessellationEvaluationShader(Raz::TessellationEvaluationShader::loadFromSource(R"(
layout(isolines, fractional_odd_spacing, ccw) in;
struct MeshInfo {
vec3 vertPosition;
vec2 vertTexcoords;
mat3 vertTBNMatrix;
};
in MeshInfo tessMeshInfo[];
uniform sampler1D uniNoiseMap;
uniform float uniSmoothness = 1.0;
uniform float uniHeight = 1.0;
uniform float uniSpeed = 1.0;
uniform float uniTime;
layout(std140) uniform uboCameraInfo {
mat4 uniViewMat;
mat4 uniInvViewMat;
mat4 uniProjectionMat;
mat4 uniInvProjectionMat;
mat4 uniViewProjectionMat;
vec3 uniCameraPos;
};
out MeshInfo vertMeshInfo;
float hash(float val) {
// "Hash without Sine", from https://www.shadertoy.com/view/4djSRW
val = fract(val * 0.1031);
val *= val + 33.33;
val *= val + val;
return fract(val);
}
void main() {
vec3 beginPos = tessMeshInfo[0].vertPosition;
vec3 endPos = tessMeshInfo[1].vertPosition;
vec3 vertPos = mix(beginPos, endPos, gl_TessCoord.x);
// Leaving the lines' extremities in place
if (gl_TessCoord.x != 0.0 && gl_TessCoord.x != 1.0) {
vertPos.y -= gl_TessCoord.y;
float time = uniTime * uniSpeed * uniSpeed;
float texcoord = mod(mix(gl_TessCoord.x, hash(time * (gl_TessCoord.y + 1.0)), 1.0 - uniSmoothness) * 10.0 + time * 0.1, 1.0);
float noise = texture(uniNoiseMap, texcoord).r * 2.0 - 1.0;
vertPos.y += noise * uniHeight;
}
vertMeshInfo.vertPosition = vertPos;
// Small hack to "fix" SSR by setting the normal pointing to the camera. This sort of prevents finding a reflection
// since nothing can exist between the point and the camera, thus displaying the line as-is
vertMeshInfo.vertTBNMatrix[2] = normalize(uniCameraPos - vertPos);
gl_Position = uniViewProjectionMat * vec4(vertPos, 1.0);
}
)"));
lightningProgram.link();
lightningProgram.setTexture(noiseTexture, "uniNoiseMap");
// Sending initial values
lightningProgram.setAttribute(0.15f, "uniHeight");
lightningProgram.setAttribute(0.035f, "uniSmoothness");
lightningProgram.sendAttributes();
////////////////////
// Post processes //
////////////////////
const auto depthBuffer = Raz::Texture2D::create(window.getWidth(), window.getHeight(), Raz::TextureColorspace::DEPTH);
const auto colorBuffer = Raz::Texture2D::create(window.getWidth(), window.getHeight(), Raz::TextureColorspace::RGB, Raz::TextureDataType::FLOAT16);
const auto blurredColorBuffer = Raz::Texture2D::create(window.getWidth(), window.getHeight(), Raz::TextureColorspace::RGB, Raz::TextureDataType::FLOAT16);
const auto colorBuffer2 = Raz::Texture2D::create(window.getWidth(), window.getHeight(), Raz::TextureColorspace::RGB, Raz::TextureDataType::FLOAT16);
const auto normalBuffer = Raz::Texture2D::create(window.getWidth(), window.getHeight(), Raz::TextureColorspace::RGB);
const auto specularBuffer = Raz::Texture2D::create(window.getWidth(), window.getHeight(), Raz::TextureColorspace::RGBA);
Raz::RenderPass& geometryPass = render.getGeometryPass();
geometryPass.setWriteDepthTexture(depthBuffer);
geometryPass.addWriteColorTexture(colorBuffer, 0);
geometryPass.addWriteColorTexture(normalBuffer, 1);
geometryPass.addWriteColorTexture(specularBuffer, 2);
auto& blur = render.getRenderGraph().addRenderProcess<Raz::BoxBlurRenderProcess>();
blur.addParent(geometryPass);
blur.setInputBuffer(colorBuffer);
blur.setOutputBuffer(blurredColorBuffer);
blur.setStrength(8);
auto& ssr = render.getRenderGraph().addRenderProcess<Raz::SsrRenderProcess>();
ssr.addParent(blur);
ssr.setInputDepthBuffer(depthBuffer);
ssr.setInputColorBuffer(colorBuffer);
ssr.setInputBlurredColorBuffer(blurredColorBuffer);
ssr.setInputNormalBuffer(normalBuffer);
ssr.setInputSpecularBuffer(specularBuffer);
ssr.setOutputBuffer(colorBuffer2);
auto& bloom = render.getRenderGraph().addRenderProcess<Raz::BloomRenderProcess>();
bloom.addParent(ssr);
bloom.setInputColorBuffer(colorBuffer2);
////////////
// Lights //
////////////
auto& leftLight = world.addEntityWithComponent<Raz::Transform>(Raz::Vec3f(-0.95f, 0.f, 0.f)).addComponent<Raz::Light>(Raz::LightType::POINT,
0.1f,
Raz::ColorPreset::MediumBlue);
auto& rightLight = world.addEntityWithComponent<Raz::Transform>(Raz::Vec3f(0.95f, 0.f, 0.f)).addComponent<Raz::Light>(Raz::LightType::POINT,
0.1f,
Raz::ColorPreset::MediumBlue);
auto& backLeftLight = world.addEntityWithComponent<Raz::Transform>(Raz::Vec3f(-0.95f, 0.f, 10.1f)).addComponent<Raz::Light>(Raz::LightType::POINT,
0.1f,
Raz::ColorPreset::MediumBlue);
auto& backRightLight = world.addEntityWithComponent<Raz::Transform>(Raz::Vec3f(0.95f, 0.f, 10.1f)).addComponent<Raz::Light>(Raz::LightType::POINT,
0.1f,
Raz::ColorPreset::MediumBlue);
///////////////////
// Key callbacks //
///////////////////
int lineCount = 1;
window.addKeyCallback(Raz::Keyboard::PAGEUP, [&lineCount, &lightningProgram] (float) {
lineCount = std::min(64, lineCount + 1);
lightningProgram.setAttribute(static_cast<float>(lineCount), "uniLineCount");
lightningProgram.sendAttributes();
}, Raz::Input::ONCE);
window.addKeyCallback(Raz::Keyboard::PAGEDOWN, [&lineCount, &lightningProgram] (float) {
lineCount = std::max(1, lineCount - 1);
lightningProgram.setAttribute(static_cast<float>(lineCount), "uniLineCount");
lightningProgram.sendAttributes();
}, Raz::Input::ONCE);
bool isSlowedDown = false;
window.addKeyCallback(Raz::Keyboard::R, [&isSlowedDown, &lightningProgram] (float) {
isSlowedDown = !isSlowedDown;
lightningProgram.setAttribute((isSlowedDown ? 0.055f : 1.f), "uniSpeed");
lightningProgram.sendAttributes();
}, Raz::Input::ONCE);
app.run([&] (float deltaTime) {
static float time = 0.f;
time += deltaTime;
lightningProgram.setAttribute(time, "uniTime");
lightningProgram.sendAttributes();
// Making the lights flicker
static std::mt19937 gen(std::random_device{}());
static std::uniform_real_distribution<float> distrib(0.1f, 0.25f);
leftLight.setEnergy(distrib(gen));
rightLight.setEnergy(distrib(gen));
backLeftLight.setEnergy(distrib(gen));
backRightLight.setEnergy(distrib(gen));
render.updateLights();
});
return EXIT_SUCCESS;
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(r16f, binding = 0) uniform writeonly image1D uniNoiseMap;
uniform float uniNoiseFactor = 0.01;
uniform int uniOctaveCount = 1;
const int permutations[512] = int[](
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117,
35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71,
134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41,
55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89,
18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226,
250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182,
189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43,
172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97,
228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239,
107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180,
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117,
35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71,
134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41,
55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89,
18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226,
250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182,
189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43,
172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97,
228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239,
107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
);
float smootherstep(float value) {
return value * value * value * (value * (value * 6.0 - 15.0) + 10.0);
}
float recoverGradient1D(int x) {
return (permutations[x] % 2 == 0 ? 1.0 : -1.0);
}
float computePerlin(float coord) {
// Determining coordinates on the line
//
// x0---------x0+1
int intX = int(coord);
int x0 = intX & 255;
float leftGrad = recoverGradient1D(x0);
float rightGrad = recoverGradient1D(x0 + 1);
// Computing the distance to the coordinate
//
// |------X--|
// xWeight
float xWeight = coord - float(intX);
float leftDot = xWeight * leftGrad;
float rightDot = (xWeight - 1) * rightGrad;
float smoothX = smootherstep(xWeight);
return mix(leftDot, rightDot, smoothX);
}
float computeFbm(float coord, int octaveCount) {
float frequency = 1.0;
float amplitude = 1.0;
float total = 0.0;
for (int i = 0; i < octaveCount; ++i) {
total += computePerlin(coord * frequency) * amplitude;
frequency *= 2.0;
amplitude *= 0.5;
}
return (total + 1.0) / 2.0;
}
void main() {
float noise = computeFbm(float(gl_GlobalInvocationID.x) * uniNoiseFactor, uniOctaveCount);
int pixelCoord = int(gl_GlobalInvocationID.x);
imageStore(uniNoiseMap, pixelCoord, vec4(vec3(noise), 1.0));
}
- Home
- How to build RaZ
- Getting started
- General usage knowledge
- Some examples...
- Playground
- Tutorials
- File formats
- Modules
- Debug