From 120f01f32890fce6e9f75c6191ac4b906986a3c3 Mon Sep 17 00:00:00 2001 From: DMT Date: Sun, 29 Dec 2024 20:59:46 -0800 Subject: [PATCH] start to refactor shaders into shader array --- renderlib/graphics/glsl/CMakeLists.txt | 8 +- renderlib/graphics/glsl/shaders.cpp | 36 +++ renderlib/graphics/glsl/shaders.h | 11 + .../graphics/glsl/shaders/CMakeLists.txt | 29 +++ renderlib/graphics/glsl/shaders/shader.frag | 200 +++++++++++++++++ .../graphics/glsl/shaders/shader_gen.hpp | 207 ++++++++++++++++++ renderlib/renderlib.cpp | 8 + 7 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 renderlib/graphics/glsl/shaders.cpp create mode 100644 renderlib/graphics/glsl/shaders.h create mode 100644 renderlib/graphics/glsl/shaders/CMakeLists.txt create mode 100644 renderlib/graphics/glsl/shaders/shader.frag create mode 100644 renderlib/graphics/glsl/shaders/shader_gen.hpp diff --git a/renderlib/graphics/glsl/CMakeLists.txt b/renderlib/graphics/glsl/CMakeLists.txt index ae045b0d..ad275b7f 100644 --- a/renderlib/graphics/glsl/CMakeLists.txt +++ b/renderlib/graphics/glsl/CMakeLists.txt @@ -1,6 +1,9 @@ target_include_directories(renderlib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" ) + +add_subdirectory(shaders) + target_sources(renderlib PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/GLBasicVolumeShader.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/GLBasicVolumeShader.h" @@ -17,5 +20,8 @@ target_sources(renderlib PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/GLFlatShader2D.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/GLFlatShader2D.h" "${CMAKE_CURRENT_SOURCE_DIR}/GLImageShader2DnoLut.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/GLImageShader2DnoLut.h" + "${CMAKE_CURRENT_SOURCE_DIR}/GLImageShader2DnoLut.h" + "${CMAKE_CURRENT_SOURCE_DIR}/shaders.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/shaders.h" ) + diff --git a/renderlib/graphics/glsl/shaders.cpp b/renderlib/graphics/glsl/shaders.cpp new file mode 100644 index 00000000..098d3bcb --- /dev/null +++ b/renderlib/graphics/glsl/shaders.cpp @@ -0,0 +1,36 @@ +#include "shaders.h" + +#include "shaders/shader_gen.hpp" + +static std::unordered_map shader_src = { { "shader", shader_shader } }; +static std::unordered_map shaders; + +bool +ShaderArray::BuildShaders() +{ + for (auto& shader : shader_src) { + GLShader* s = new GLShader(GL_FRAGMENT_SHADER); + if (s->compileSourceCode(shader.second.c_str())) { + shaders[shader.first] = s; + } else { + LOG_ERROR << "Failed to compile shader " << shader.first; + return false; + } + } + return true; +} + +GLShader* +ShaderArray::GetShader(const std::string& name) +{ + return shaders[name]; +} + +void +ShaderArray::DestroyShaders() +{ + for (auto& shader : shaders) { + delete shader.second; + } + shaders.clear(); +} \ No newline at end of file diff --git a/renderlib/graphics/glsl/shaders.h b/renderlib/graphics/glsl/shaders.h new file mode 100644 index 00000000..97b3f4c2 --- /dev/null +++ b/renderlib/graphics/glsl/shaders.h @@ -0,0 +1,11 @@ +#pragma once + +#include "gl/Util.h" + +class ShaderArray +{ +public: + static bool BuildShaders(); + static GLShader* GetShader(const std::string& name); + static void DestroyShaders(); +}; diff --git a/renderlib/graphics/glsl/shaders/CMakeLists.txt b/renderlib/graphics/glsl/shaders/CMakeLists.txt new file mode 100644 index 00000000..6fb75e2f --- /dev/null +++ b/renderlib/graphics/glsl/shaders/CMakeLists.txt @@ -0,0 +1,29 @@ +function(convert_shader_to_cpp input_file output_file) + # Read the shader file content + file(READ "${input_file}" shader_content) + + # Escape special characters in the shader content + #string(REGEX REPLACE "\\" "\\\\" shader_content "${shader_content}") + #string(REGEX REPLACE "\"" "\\\"" shader_content "${shader_content}") + #string(REGEX REPLACE "\n" "\\n" shader_content "${shader_content}") + string(REPLACE "." "_" output_file "${output_file}") + + # Generate the C++ file content + set(cpp_content " +#include + +const std::string ${output_file}_shader = R\"( +${shader_content} +)\"; + +") + + # Write the C++ file + file(WRITE "${output_file}_gen.hpp" "${cpp_content}") +endfunction() + +# Convert your shader file +convert_shader_to_cpp("shader.frag" "shader") + +# Add the generated C++ file to your target +target_sources(renderlib PRIVATE shader_gen.hpp) diff --git a/renderlib/graphics/glsl/shaders/shader.frag b/renderlib/graphics/glsl/shaders/shader.frag new file mode 100644 index 00000000..26458960 --- /dev/null +++ b/renderlib/graphics/glsl/shaders/shader.frag @@ -0,0 +1,200 @@ +#version 400 core + +in VertexData +{ + vec3 pObj; +} inData; + +out vec4 outputColour; + +uniform mat4 inverseModelViewMatrix; + +uniform sampler3D textureAtlas; +uniform sampler2D textureAtlasMask; +//uniform sampler2D lut; + +#define M_PI 3.14159265358979323846 +uniform vec2 iResolution; +uniform float isPerspective; +uniform float orthoScale; +uniform float GAMMA_MIN; +uniform float GAMMA_MAX; +uniform float GAMMA_SCALE; +uniform float BRIGHTNESS; +uniform float DENSITY; +uniform float maskAlpha; +uniform int BREAK_STEPS; +uniform vec3 AABB_CLIP_MIN; +uniform vec3 AABB_CLIP_MAX; +uniform vec3 flipVolumeAxes; + +uniform float dataRangeMin; // 0..1 (mapped from 0..uint16_max) +uniform float dataRangeMax; // 0..1 (mapped from 0..uint16_max) + +uniform vec4 g_clipPlane; + +float powf(float a, float b) { + return pow(a, b); +} + +float rand(vec2 co) { + float threadId = gl_FragCoord.x / (gl_FragCoord.y + 1.0); + float bigVal = threadId*1299721.0 / 911.0; + vec2 smallVal = vec2(threadId*7927.0 / 577.0, threadId*104743.0 / 1039.0); + return fract(sin(dot(co, smallVal)) * bigVal); +} + +vec4 luma2Alpha(vec4 color, float vmin, float vmax, float C) { + float x = max(color[2], max(color[0], color[1])); + float xi = (x - vmin) / (vmax - vmin); + xi = clamp(xi, 0.0, 1.0); + float y = pow(xi, C); + y = clamp(y, 0.0, 1.0); + color[3] = y; + return(color); +} + +vec4 sampleAs3DTexture(sampler3D tex, vec4 pos) { + float bounds = float(pos[0] > 0.001 && pos[0] < 0.999 && + pos[1] > 0.001 && pos[1] < 0.999 && + pos[2] > 0.001 && pos[2] < 0.999); + + vec4 texval = textureLod(tex, pos.xyz*flipVolumeAxes, 0).rgba; + vec4 retval = vec4(texval.rgb, 1.0); + +// float texval = textureLod(tex, pos.xyz, 0).r; +// texval = (texval - dataRangeMin) / (dataRangeMax - dataRangeMin); +// vec4 retval = vec4(texval, texval, texval, 1.0); + return bounds*retval; +} + +vec4 sampleStack(sampler3D tex, vec4 pos) { + vec4 col = sampleAs3DTexture(tex, pos); + col = luma2Alpha(col, GAMMA_MIN, GAMMA_MAX, GAMMA_SCALE); + return col; +} + +//->intersect AXIS-ALIGNED box routine +// +bool intersectBox(in vec3 r_o, in vec3 r_d, in vec3 boxMin, in vec3 boxMax, out float tnear, out float tfar) { + vec3 invR = vec3(1.0, 1.0, 1.0) / r_d; + vec3 tbot = invR * (boxMin - r_o); + vec3 ttop = invR * (boxMax - r_o); + vec3 tmin = min(ttop, tbot); + vec3 tmax = max(ttop, tbot); + float largest_tmin = max(max(tmin.x, tmin.y), max(tmin.x, tmin.z)); + float smallest_tmax = min(min(tmax.x, tmax.y), min(tmax.x, tmax.z)); + tnear = largest_tmin; + tfar = smallest_tmax; + + // now constrain near and far using clipPlane if active. + // plane xyz is normal, plane w is -distance from origin. + float denom = dot(r_d, g_clipPlane.xyz); + if (abs(denom) > 0.0001f) // your favorite epsilon + { + float tClip = dot(g_clipPlane.xyz*(-g_clipPlane.w) - r_o, g_clipPlane.xyz) / denom; + if (denom < 0.0f) { + tnear = max(tnear, tClip); + } + else { + tfar = min(tfar, tClip); + } + } + else + { + // todo check to see which side of the plane we are on ? + } + + + return(tfar > tnear); +} + +vec4 integrateVolume(vec4 eye_o, vec4 eye_d, + float tnear, float tfar, + float clipNear, float clipFar, + sampler3D textureAtlas +) { + vec4 C = vec4(0.0); + float tend = min(tfar, clipFar); + float tbegin = tnear; + const int maxSteps = 512; + float csteps = clamp(float(BREAK_STEPS), 1.0, float(maxSteps)); + float invstep = 1.0 / csteps; + float r = 0.5 - 1.0*rand(eye_d.xy); + float tstep = invstep; + float tfarsurf = r*tstep; + float overflow = mod((tfarsurf - tend), tstep); + float t = tbegin + overflow; + t += r*tstep; + float tdist = 0.0; + int numSteps = 0; + + vec4 pos, col; + float s = 0.5 * float(maxSteps) / csteps; + for (int i = 0; i tend) + break; + if (C.w > 1.0) + break; + } + return C; +} +void main() +{ + outputColour = vec4(1.0, 0.0, 0.0, 1.0); + // gl_FragCoord defaults to 0,0 at lower left + vec2 vUv = gl_FragCoord.xy/iResolution.xy; + + vec3 eyeRay_o, eyeRay_d; + if (isPerspective != 0.0) { + // camera position in camera space is 0,0,0! + eyeRay_o = (inverseModelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz; + eyeRay_d = normalize(inData.pObj - eyeRay_o); + } + else { + float zDist = 2.0; + eyeRay_d = (inverseModelViewMatrix*vec4(0.0, 0.0, -zDist, 0.0)).xyz; + vec4 ray_o = vec4(2.0*vUv - 1.0, 1.0, 1.0); + ray_o.xy *= orthoScale; + ray_o.x *= iResolution.x/iResolution.y; + eyeRay_o = (inverseModelViewMatrix*ray_o).xyz; + } + + vec3 boxMin = AABB_CLIP_MIN; + vec3 boxMax = AABB_CLIP_MAX; + float tnear, tfar; + bool hit = intersectBox(eyeRay_o, eyeRay_d, boxMin, boxMax, tnear, tfar); + if (!hit) { + outputColour = vec4(1.0, 0.0, 1.0, 0.0); + return; + } +//else { +// outputColour = vec4(1.0, 1.0, 1.0, 1.0); +// return; +//} + float clipNear = 0.0;//-(dot(eyeRay_o.xyz, eyeNorm) + dNear) / dot(eyeRay_d.xyz, eyeNorm); + float clipFar = 10000.0;//-(dot(eyeRay_o.xyz,-eyeNorm) + dFar ) / dot(eyeRay_d.xyz,-eyeNorm); + + vec4 C = integrateVolume(vec4(eyeRay_o, 1.0), vec4(eyeRay_d, 0.0), + tnear, tfar, + clipNear, clipFar, + textureAtlas); + C = clamp(C, 0.0, 1.0); + outputColour = C; + return; +} diff --git a/renderlib/graphics/glsl/shaders/shader_gen.hpp b/renderlib/graphics/glsl/shaders/shader_gen.hpp new file mode 100644 index 00000000..dd91d261 --- /dev/null +++ b/renderlib/graphics/glsl/shaders/shader_gen.hpp @@ -0,0 +1,207 @@ + +#include + +const std::string shader_shader = R"( +#version 400 core + +in VertexData +{ + vec3 pObj; +} inData; + +out vec4 outputColour; + +uniform mat4 inverseModelViewMatrix; + +uniform sampler3D textureAtlas; +uniform sampler2D textureAtlasMask; +//uniform sampler2D lut; + +#define M_PI 3.14159265358979323846 +uniform vec2 iResolution; +uniform float isPerspective; +uniform float orthoScale; +uniform float GAMMA_MIN; +uniform float GAMMA_MAX; +uniform float GAMMA_SCALE; +uniform float BRIGHTNESS; +uniform float DENSITY; +uniform float maskAlpha; +uniform int BREAK_STEPS; +uniform vec3 AABB_CLIP_MIN; +uniform vec3 AABB_CLIP_MAX; +uniform vec3 flipVolumeAxes; + +uniform float dataRangeMin; // 0..1 (mapped from 0..uint16_max) +uniform float dataRangeMax; // 0..1 (mapped from 0..uint16_max) + +uniform vec4 g_clipPlane; + +float powf(float a, float b) { + return pow(a, b); +} + +float rand(vec2 co) { + float threadId = gl_FragCoord.x / (gl_FragCoord.y + 1.0); + float bigVal = threadId*1299721.0 / 911.0; + vec2 smallVal = vec2(threadId*7927.0 / 577.0, threadId*104743.0 / 1039.0); + return fract(sin(dot(co, smallVal)) * bigVal); +} + +vec4 luma2Alpha(vec4 color, float vmin, float vmax, float C) { + float x = max(color[2], max(color[0], color[1])); + float xi = (x - vmin) / (vmax - vmin); + xi = clamp(xi, 0.0, 1.0); + float y = pow(xi, C); + y = clamp(y, 0.0, 1.0); + color[3] = y; + return(color); +} + +vec4 sampleAs3DTexture(sampler3D tex, vec4 pos) { + float bounds = float(pos[0] > 0.001 && pos[0] < 0.999 && + pos[1] > 0.001 && pos[1] < 0.999 && + pos[2] > 0.001 && pos[2] < 0.999); + + vec4 texval = textureLod(tex, pos.xyz*flipVolumeAxes, 0).rgba; + vec4 retval = vec4(texval.rgb, 1.0); + +// float texval = textureLod(tex, pos.xyz, 0).r; +// texval = (texval - dataRangeMin) / (dataRangeMax - dataRangeMin); +// vec4 retval = vec4(texval, texval, texval, 1.0); + return bounds*retval; +} + +vec4 sampleStack(sampler3D tex, vec4 pos) { + vec4 col = sampleAs3DTexture(tex, pos); + col = luma2Alpha(col, GAMMA_MIN, GAMMA_MAX, GAMMA_SCALE); + return col; +} + +//->intersect AXIS-ALIGNED box routine +// +bool intersectBox(in vec3 r_o, in vec3 r_d, in vec3 boxMin, in vec3 boxMax, out float tnear, out float tfar) { + vec3 invR = vec3(1.0, 1.0, 1.0) / r_d; + vec3 tbot = invR * (boxMin - r_o); + vec3 ttop = invR * (boxMax - r_o); + vec3 tmin = min(ttop, tbot); + vec3 tmax = max(ttop, tbot); + float largest_tmin = max(max(tmin.x, tmin.y), max(tmin.x, tmin.z)); + float smallest_tmax = min(min(tmax.x, tmax.y), min(tmax.x, tmax.z)); + tnear = largest_tmin; + tfar = smallest_tmax; + + // now constrain near and far using clipPlane if active. + // plane xyz is normal, plane w is -distance from origin. + float denom = dot(r_d, g_clipPlane.xyz); + if (abs(denom) > 0.0001f) // your favorite epsilon + { + float tClip = dot(g_clipPlane.xyz*(-g_clipPlane.w) - r_o, g_clipPlane.xyz) / denom; + if (denom < 0.0f) { + tnear = max(tnear, tClip); + } + else { + tfar = min(tfar, tClip); + } + } + else + { + // todo check to see which side of the plane we are on ? + } + + + return(tfar > tnear); +} + +vec4 integrateVolume(vec4 eye_o, vec4 eye_d, + float tnear, float tfar, + float clipNear, float clipFar, + sampler3D textureAtlas +) { + vec4 C = vec4(0.0); + float tend = min(tfar, clipFar); + float tbegin = tnear; + const int maxSteps = 512; + float csteps = clamp(float(BREAK_STEPS), 1.0, float(maxSteps)); + float invstep = 1.0 / csteps; + float r = 0.5 - 1.0*rand(eye_d.xy); + float tstep = invstep; + float tfarsurf = r*tstep; + float overflow = mod((tfarsurf - tend), tstep); + float t = tbegin + overflow; + t += r*tstep; + float tdist = 0.0; + int numSteps = 0; + + vec4 pos, col; + float s = 0.5 * float(maxSteps) / csteps; + for (int i = 0; i tend) + break; + if (C.w > 1.0) + break; + } + return C; +} +void main() +{ + outputColour = vec4(1.0, 0.0, 0.0, 1.0); + // gl_FragCoord defaults to 0,0 at lower left + vec2 vUv = gl_FragCoord.xy/iResolution.xy; + + vec3 eyeRay_o, eyeRay_d; + if (isPerspective != 0.0) { + // camera position in camera space is 0,0,0! + eyeRay_o = (inverseModelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz; + eyeRay_d = normalize(inData.pObj - eyeRay_o); + } + else { + float zDist = 2.0; + eyeRay_d = (inverseModelViewMatrix*vec4(0.0, 0.0, -zDist, 0.0)).xyz; + vec4 ray_o = vec4(2.0*vUv - 1.0, 1.0, 1.0); + ray_o.xy *= orthoScale; + ray_o.x *= iResolution.x/iResolution.y; + eyeRay_o = (inverseModelViewMatrix*ray_o).xyz; + } + + vec3 boxMin = AABB_CLIP_MIN; + vec3 boxMax = AABB_CLIP_MAX; + float tnear, tfar; + bool hit = intersectBox(eyeRay_o, eyeRay_d, boxMin, boxMax, tnear, tfar); + if (!hit) { + outputColour = vec4(1.0, 0.0, 1.0, 0.0); + return; + } +//else { +// outputColour = vec4(1.0, 1.0, 1.0, 1.0); +// return; +//} + float clipNear = 0.0;//-(dot(eyeRay_o.xyz, eyeNorm) + dNear) / dot(eyeRay_d.xyz, eyeNorm); + float clipFar = 10000.0;//-(dot(eyeRay_o.xyz,-eyeNorm) + dFar ) / dot(eyeRay_d.xyz,-eyeNorm); + + vec4 C = integrateVolume(vec4(eyeRay_o, 1.0), vec4(eyeRay_d, 0.0), + tnear, tfar, + clipNear, clipFar, + textureAtlas); + C = clamp(C, 0.0, 1.0); + outputColour = C; + return; +} + +)"; + diff --git a/renderlib/renderlib.cpp b/renderlib/renderlib.cpp index f5ba922a..44b99082 100644 --- a/renderlib/renderlib.cpp +++ b/renderlib/renderlib.cpp @@ -5,6 +5,7 @@ #include "Logging.h" #include "RenderGL.h" #include "RenderGLPT.h" +#include "shaders.h" #include #include @@ -293,6 +294,11 @@ renderlib::initialize(std::string assetPath, bool headless, bool listDevices, in LOG_INFO << "GL_VENDOR: " << std::string((char*)glGetString(GL_VENDOR)); LOG_INFO << "GL_RENDERER: " << std::string((char*)glGetString(GL_RENDERER)); + LOG_INFO << "Compiling all shaders..."; + if (!ShaderArray::BuildShaders()) { + status = 0; + } + delete dummyHeadlessContext; return status; } @@ -323,6 +329,8 @@ renderlib::cleanup() clearGpuVolumeCache(); + ShaderArray::DestroyShaders(); + delete dummySurface; dummySurface = nullptr; delete dummyContext;