// SPDX-License-Identifier: LGPL-2.1-or-later AND MIT /*************************************************************************** * Copyright (c) 2024 Shai Seger * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * * * * Portions of this code are taken from: * * "OpenGL 4 Shading Language cookbook" Third edition * * Written by: David Wolff * * Published by: www.packt.com * * License: MIT License * * * * * ***************************************************************************/ #include "GlUtils.h" #include "Shader.h" #include #include namespace MillSim { Shader* CurrentShader = nullptr; Shader::~Shader() { Destroy(); } void Shader::UpdateModelMat(mat4x4 tmat, mat4x4 nmat) { if (mModelPos >= 0) { glUniformMatrix4fv(mModelPos, 1, GL_FALSE, (GLfloat*)tmat); } if (mNormalRotPos >= 0) { glUniformMatrix4fv(mNormalRotPos, 1, GL_FALSE, (GLfloat*)nmat); } } void Shader::UpdateProjectionMat(mat4x4 mat) { if (mProjectionPos >= 0) { glUniformMatrix4fv(mProjectionPos, 1, GL_FALSE, (GLfloat*)mat); } } void Shader::UpdateViewMat(mat4x4 mat) { if (mViewPos >= 0) { glUniformMatrix4fv(mViewPos, 1, GL_FALSE, (GLfloat*)mat); } } void Shader::UpdateEnvColor(vec3 lightPos, vec3 lightColor, vec3 ambient, float linearity) { if (mLightPosPos >= 0) { glUniform3fv(mLightPosPos, 1, lightPos); } if (mLightColorPos >= 0) { glUniform3fv(mLightColorPos, 1, lightColor); } if (mLightAmbientPos >= 0) { glUniform3fv(mLightAmbientPos, 1, ambient); } if (mLightLinearPos >= 0) { glUniform1f(mLightLinearPos, linearity); } } void Shader::UpdateScreenDimension(int width, int height) { if (mScreenWidthPos >= 0) { glUniform1f(mScreenWidthPos, (float)width); } if (mScreenHeightPos >= 0) { glUniform1f(mScreenHeightPos, (float)height); } } void Shader::UpdateObjColor(vec3 objColor) { if (mObjectColorPos >= 0) { glUniform3fv(mObjectColorPos, 1, objColor); } } void Shader::UpdateObjColorAlpha(vec4 objColor) { if (mObjectColorAlphaPos >= 0) { glUniform4fv(mObjectColorAlphaPos, 1, objColor); } } void Shader::UpdateNormalState(bool isInverted) { if (mInvertedNormalsPos >= 0) { glUniform1i(mInvertedNormalsPos, isInverted); } } void Shader::UpdateSsaoActive(bool isActive) { if (mSsaoActivePos >= 0) { glUniform1i(mSsaoActivePos, isActive); } } void Shader::UpdateTextureSlot(int slot) { if (mTexSlotPos >= 0) { glUniform1i(mTexSlotPos, slot); } } void Shader::UpdateColorTexSlot(int albedoSlot) { if (mAlbedoPos >= 0) { glUniform1i(mAlbedoPos, albedoSlot); } } void Shader::UpdatePositionTexSlot(int positionSlot) { if (mPositionPos >= 0) { glUniform1i(mPositionPos, positionSlot); } } void Shader::UpdateNormalTexSlot(int normalSlot) { if (mNormalPos >= 0) { glUniform1i(mNormalPos, normalSlot); } } void Shader::UpdateRandomTexSlot(int randSlot) { if (mRandTexPos >= 0) { glUniform1i(mRandTexPos, randSlot); } } void Shader::UpdateSsaoTexSlot(int ssaoSlot) { if (mSsaoPos >= 0) { glUniform1i(mSsaoPos, ssaoSlot); } } void Shader::UpdateKernelVals(int nVals, float* vals) { glUniform3fv(mSamplesPos, nVals, vals); } void Shader::UpdateCurSegment(int curSeg) { if (mCurSegmentPos >= 0) { glUniform1i(mCurSegmentPos, curSeg); } } bool CheckCompileResult(int shaderId, const char* shaderName, bool isVertex) { char log[1024]; int res = 0; GLsizei len; glGetShaderiv(shaderId, GL_COMPILE_STATUS, &res); if (res != 0) { return false; } int headerLen = snprintf(log, 48, "Error compiling %s %s shader: ", shaderName, isVertex ? "vertex" : "fragment"); glGetShaderInfoLog(shaderId, 1020 - headerLen, &len, log + headerLen); len += headerLen; if (len > 1020) { len = 1020; } log[len] = 0; Base::Console().error(log); return true; } unsigned int Shader::CompileShader(const char* name, const char* _vertShader, const char* _fragShader) { vertShader = _vertShader; fragShader = _fragShader; const GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); GLint res = 0; glShaderSource(vertex_shader, 1, &vertShader, NULL); glCompileShader(vertex_shader); if (CheckCompileResult(vertex_shader, name, true)) { glDeleteShader(vertex_shader); return 0xdeadbeef; } const GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader, 1, &fragShader, NULL); glCompileShader(fragment_shader); if (CheckCompileResult(fragment_shader, name, false)) { glDeleteShader(fragment_shader); glDeleteShader(vertex_shader); return 0xdeadbeef; } shaderId = glCreateProgram(); glAttachShader(shaderId, vertex_shader); glAttachShader(shaderId, fragment_shader); glLinkProgram(shaderId); glGetProgramiv(shaderId, GL_LINK_STATUS, &res); if (res == 0) { Destroy(); return 0xdeadbeef; } // get all uniform parameters positions mModelPos = glGetUniformLocation(shaderId, "model"); mNormalRotPos = glGetUniformLocation(shaderId, "normalRot"); mProjectionPos = glGetUniformLocation(shaderId, "projection"); mViewPos = glGetUniformLocation(shaderId, "view"); mLightPosPos = glGetUniformLocation(shaderId, "lightPos"); mLightColorPos = glGetUniformLocation(shaderId, "lightColor"); mLightLinearPos = glGetUniformLocation(shaderId, "lightLinear"); mLightAmbientPos = glGetUniformLocation(shaderId, "lightAmbient"); mObjectColorPos = glGetUniformLocation(shaderId, "objectColor"); mObjectColorAlphaPos = glGetUniformLocation(shaderId, "objectColorAlpha"); mTexSlotPos = glGetUniformLocation(shaderId, "texSlot"); mInvertedNormalsPos = glGetUniformLocation(shaderId, "invertedNormals"); mSsaoActivePos = glGetUniformLocation(shaderId, "ssaoActive"); mAlbedoPos = glGetUniformLocation(shaderId, "ColorTex"); mPositionPos = glGetUniformLocation(shaderId, "PositionTex"); mNormalPos = glGetUniformLocation(shaderId, "NormalTex"); mSsaoPos = glGetUniformLocation(shaderId, "AoTex"); mRandTexPos = glGetUniformLocation(shaderId, "RandTex"); mSamplesPos = glGetUniformLocation(shaderId, "SampleKernel"); mCurSegmentPos = glGetUniformLocation(shaderId, "curSegment"); mScreenWidthPos = glGetUniformLocation(shaderId, "screenWidth"); mScreenHeightPos = glGetUniformLocation(shaderId, "screenHeight"); Activate(); return shaderId; } void Shader::Activate() { if (shaderId > 0) { glUseProgram(shaderId); } CurrentShader = this; } void Shader::Destroy() { if (shaderId == 0) { return; } glDeleteProgram(shaderId); shaderId = 0; } const char* VertShader3DNorm = R"( #version 330 core layout(location = 0) in vec3 aPosition; layout(location = 1) in vec3 aNormal; out vec3 Normal; out vec3 Position; uniform mat4 model; uniform mat4 view; uniform mat4 projection; uniform mat4 normalRot; void main(void) { vec4 viewPos = view * model * vec4(aPosition, 1.0); Position = vec3(model * vec4(aPosition, 1.0)); Normal = vec3(normalRot * vec4(aNormal, 1.0)); gl_Position = projection * viewPos; } )"; const char* VertShader3DInvNorm = R"( #version 330 core layout(location = 0) in vec3 aPosition; layout(location = 1) in vec3 aNormal; out vec3 Normal; out vec3 Position; uniform mat4 model; uniform mat4 view; uniform mat4 projection; uniform mat4 normalRot; void main(void) { gl_Position = projection * view * model * vec4(aPosition, 1.0); Position = vec3(model * vec4(aPosition, 1.0)); Normal = -vec3(normalRot * vec4(aNormal, 1.0)); } )"; const char* VertShader2DTex = R"( #version 330 core layout(location = 0) in vec2 aPosition; layout(location = 1) in vec2 aTexCoord; out vec2 texCoord; uniform mat4 projection; uniform mat4 model; void main(void) { gl_Position = projection * model * vec4(aPosition, 0.0, 1.0); texCoord = aTexCoord; } )"; const char* FragShader2dTex = R"( #version 330 out vec4 FragColor; in vec2 texCoord; uniform vec3 objectColor; uniform sampler2D texSlot; void main() { vec4 texColor = texture(texSlot, texCoord); FragColor = vec4(objectColor, 1.0) * texColor; } )"; const char* FragShaderNorm = R"( #version 330 out vec4 FragColor; in vec3 Normal; in vec3 Position; uniform vec3 lightPos; uniform vec3 lightColor; uniform vec3 objectColor; uniform vec3 lightAmbient; void main() { vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - Position); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor; vec3 result = (lightAmbient + diffuse) * objectColor; FragColor = vec4(result, 1.0); } )"; const char* FragShaderFlat = R"( #version 330 out vec4 FragColor; in vec3 Normal; in vec3 Position; uniform vec3 objectColor; void main() { FragColor = vec4(objectColor, 1.0); } )"; const char* VertShader2DFbo = R"( #version 330 core layout(location = 0) in vec2 aPosition; layout(location = 1) in vec2 aTexCoord; out vec2 texCoord; void main(void) { gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0); texCoord = aTexCoord; } )"; const char* FragShader2dFbo = R"( #version 330 out vec4 FragColor; in vec2 texCoord; uniform sampler2D texSlot; void main() { vec4 tc = texture(texSlot, texCoord); FragColor = tc; } )"; const char* VertShaderGeom = R"( #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; out vec3 Position; out vec3 Normal; uniform bool invertedNormals; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { vec4 viewPos = view * model * vec4(aPos, 1.0); Position = viewPos.xyz; mat3 normalMatrix = transpose(inverse(mat3(view * model))); Normal = normalMatrix * (invertedNormals ? -aNormal : aNormal); gl_Position = projection * viewPos; } )"; const char* FragShaderGeom = R"( #version 330 core layout (location = 0) out vec4 ColorTex; layout (location = 1) out vec3 PositionTex; layout (location = 2) out vec3 NormalTex; in vec3 Position; in vec3 Normal; uniform vec3 objectColor; void main() { // Store position, normal, and diffuse color in textures PositionTex = Position; NormalTex = normalize(Normal); ColorTex = vec4(objectColor, 1.0f); } )"; const char* FragShaderSSAO = R"( #version 330 core layout (location = 0) out float AoData; in vec2 texCoord; uniform sampler2D RandTex; uniform sampler2D PositionTex; uniform sampler2D NormalTex; const int kernelSize = 64; uniform vec3 SampleKernel[kernelSize]; uniform float Radius = 20.0f; uniform float screenWidth = 800.0; uniform float screenHeight = 600.0; uniform mat4 projection; void main() { // Create the random tangent space matrix vec2 randScale = vec2( screenWidth / 4.0, screenHeight / 4.0 ); vec3 randDir = normalize( texture(RandTex, texCoord.xy * randScale).xyz ); vec3 n = normalize( texture(NormalTex, texCoord).xyz ); vec3 biTang = cross( n, randDir ); if( length(biTang) < 0.0001 ) // If n and randDir are parallel, n is in x-y plane biTang = cross( n, vec3(0,0,1)); biTang = normalize(biTang); vec3 tang = cross(biTang, n); mat3 toCamSpace = mat3(tang, biTang, n); float occlusionSum = 0.0; vec3 camPos = texture(PositionTex, texCoord).xyz; for( int i = 0; i < kernelSize; i++ ) { vec3 samplePos = camPos + Radius * (toCamSpace * SampleKernel[i]); // Project point vec4 p = projection * vec4(samplePos,1); p *= 1.0 / p.w; p.xyz = p.xyz * 0.5 + 0.5; // Access camera space z-coordinate at that point float surfaceZ = texture(PositionTex, p.xy).z; float zDist = surfaceZ - camPos.z; // Count points that ARE occluded if( zDist >= 0.0 && zDist <= Radius && surfaceZ > samplePos.z ) occlusionSum += 1.0; } float occ = occlusionSum / kernelSize; AoData = 1.0 - occ; } )"; const char* FragShaderSSAOBlur = R"( #version 330 core layout (location = 0) out float AoData; in vec2 texCoord; uniform sampler2D AoTex; void main() { ivec2 pix = ivec2( gl_FragCoord.xy ); float sum = 0.0; for( int x = -1; x <= 2; ++x ) { for( int y = -1; y <= 2; y++ ) { sum += texelFetch( AoTex, pix + ivec2(x,y), 0).r; } } float ao = sum * (1.0 / 16.0); AoData = ao; } )"; const char* FragShaderSSAOLighting = R"( #version 330 core out vec4 FragColor; in vec2 texCoord; uniform vec3 lightPos; uniform vec3 lightColor; uniform vec3 lightAmbient; uniform bool ssaoActive; uniform sampler2D ColorTex; uniform sampler2D PositionTex; uniform sampler2D NormalTex; uniform sampler2D AoTex; vec3 ambAndDiffuse( vec3 pos, vec3 norm, vec3 diff, float ao ) { ao = pow(ao, 4); vec3 ambient = lightAmbient * diff * ao; vec3 s = normalize( lightPos - pos); float sDotN = max( dot(s,norm), 0.0 ); return ambient + lightColor * diff * sDotN; } void main() { vec3 pos = texture( PositionTex, texCoord ).xyz; vec3 norm = texture( NormalTex, texCoord ).xyz; vec4 DiffColorA = texture(ColorTex, texCoord); vec3 diffColor = DiffColorA.rgb; float aoVal = ssaoActive ? texture( AoTex, texCoord).r : 1.0; vec3 col = ambAndDiffuse(pos, norm, diffColor, aoVal); col = pow(col, vec3(1.0/2.2)); FragColor = vec4( col, DiffColorA.a ); } )"; const char* VertShader3DLine = R"( #version 330 core layout(location = 0) in vec3 aPosition; layout(location = 1) in int aIndex; flat out int Index; uniform mat4 view; uniform mat4 projection; void main(void) { gl_Position = projection * view * vec4(aPosition, 1.0); Index = aIndex; } )"; const char* FragShader3DLine = R"( #version 330 out vec4 FragColor; flat in int Index; uniform vec4 objectColorAlpha; uniform vec3 objectColor; uniform int curSegment; void main() { if (Index > curSegment) FragColor = objectColorAlpha; else FragColor = vec4(objectColor, objectColorAlpha.a); } )"; } // namespace MillSim