图形学从入门到精通:Shader编程与性能优化实战指南

管理员
## 前言:为什么选择图形学? 图形学是计算机科学的皇冠上的明珠,它融合了数学、物理、编程和艺术,是现代游戏、影视特效、数据可视化、AR/VR等领域的核心技术。从《赛博朋克2077》的令人惊叹的画面到《阿凡达》的视觉效果,从Unity/Unreal引擎的实时渲染到Blender的建模渲染,图形学无处不在。 但图形学的学习曲线陡峭,很多初学者在复杂的数学公式和晦涩的API面前望而却步。实际上,图形学的核心思想非常直观:**如何用计算机画出你看到的、想象的世界**。当你理解了光与物质的相互作用、数学与视觉的对应关系,图形学就会变得生动有趣。 读完这篇博客,你不仅能够掌握图形学的理论基础,更能够编写复杂的Shader,理解GPU渲染管线,进行性能优化,真正从入门走向精通。 --- ## 第一章:图形学基础概念 ### 1.1 什么是图形学? 计算机图形学是研究如何用计算机生成、处理、显示图形的学科。它涉及数学、物理、计算机科学等多个领域,主要研究内容包括: - **建模**:如何用数学方式描述三维物体 - **渲染**:如何将三维场景绘制到二维屏幕 - **动画**:如何让静态图形动起来 - **交互**:如何让用户与图形进行交互 ### 1.2 渲染管线基础 渲染管线是图形学的核心概念,它描述了从3D场景到2D图像的转换过程。 #### OpenGL渲染管线流程 ```glsl // 顶点着色器(Vertex Shader) #version 330 core layout (location = 0) in vec3 aPos; // 顶点位置(模型局部坐标) layout (location = 1) in vec3 aColor; // 顶点颜色 out vec3 ourColor; // 将顶点颜色作为输出传递到下一阶段,经过插值后输入片段着色器。 uniform mat4 model; // 模型变换:局部坐标 → 世界坐标 uniform mat4 view; // 视图变换:世界坐标 → 观察空间(摄像机视角) uniform mat4 projection; // 投影变换:观察空间 → 裁剪空间(透视或正交) void main() { // 裁剪空间坐标(四维向量,w分量用于后续透视除法)。 gl_Position = projection * view * model * vec4(aPos, 1.0); ourColor = aColor; } // 片段着色器(Fragment Shader) #version 330 core out vec4 FragColor; in vec3 ourColor; // 从顶点着色器经过插值后的颜色 void main() { FragColor = vec4(ourColor, 1.0); // 直接输出颜色,alpha = 1.0 } ``` 1、OpenGL渲染管线中最核心的两个可编程阶段:顶点着色器和片段着色器。 2、OpenGL渲染管线流程: ``` 顶点数据 → 顶点着色器 → 图元装配 → 光栅化 → 片段着色器 → 测试与混合 → 帧缓冲区 ``` 3.1、顶点着色器(Vertex Shader):处理每个顶点的数据,完成坐标变换和顶点属性传递。 3.2、图元装配(Primitive Assembly): ``` - 将顶点重新组织成图元(点、线、三角形等) - 执行视锥体裁剪:丢弃裁剪空间外的图元 - 透视除法:(x/w, y/w, z/w) 转换到NDC(归一化设备坐标,范围[-1,1]) - 视口变换:NDC映射到屏幕坐标 ``` 3.3、光栅化(Rasterization): ``` - 将图元分解成片段(Fragments)(候选像素) - 对顶点着色器的输出(如 ourColor)进行插值,每个片段获得自己的颜色值 ``` 3.4、片段着色器(Fragment Shader):计算每个片段的最终颜色(或深度值)。 3.5、逐片段操作(Per-Fragment Operations): ``` - 深度测试:比较当前片段的深度与深度缓冲区,决定是否丢弃 - 模板测试:基于模板缓冲区进行条件丢弃 - 混合:将片段颜色与帧缓冲区已有颜色混合(需开启透明度) ``` 3.6、最终颜色写入帧缓冲区,显示到屏幕。 #### 实例:创建第一个三角形 ```cpp // C++ OpenGL 示例 #include #include #include void framebuffer_size_callback(GLFWwindow* window, int width, int height); void processInput(GLFWwindow* window); // 着色器源码 const char* vertexShaderSource = R"( #version 330 core layout (location = 0) in vec3 aPos; void main() { gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); } )"; const char* fragmentShaderSource = R"( #version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); } )"; int main() { // 初始化GLFW glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 创建窗口 GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); if (window == NULL) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 初始化GLEW if (glewInit() != GLEW_OK) { std::cout << "Failed to initialize GLEW" << std::endl; return -1; } // 编译顶点着色器 unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); // 编译片段着色器 unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); // 创建着色器程序 unsigned int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); // 删除着色器对象 glDeleteShader(vertexShader); glDeleteShader(fragmentShader); // 设置顶点数据 float vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f }; unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); // 绑定VAO glBindVertexArray(VAO); // 绑定VBO并设置数据 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 设置顶点属性指针 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 解绑 glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); // 渲染循环 while (!glfwWindowShouldClose(window)) { // 输入处理 processInput(window); // 清屏 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // 绘制三角形 glUseProgram(shaderProgram); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3); // 交换缓冲区 glfwSwapBuffers(window); glfwPollEvents(); } // 清理资源 glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteProgram(shaderProgram); glfwTerminate(); return 0; } void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } void processInput(GLFWwindow* window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); } ``` --- ## 第二章:数学基础 ### 2.1 向量数学 向量是图形学中最基础的数学工具,用于表示位置、方向、速度等。 #### 向量操作Shader实现 ```glsl // 向量常用操作 vec3 normalize(vec3 v) { float len = length(v); return len > 0.0 ? v / len : vec3(0.0); } float dot(vec3 a, vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } vec3 cross(vec3 a, vec3 b) { return vec3( a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x ); } vec3 reflect(vec3 I, vec3 N) { return I - 2.0 * dot(N, I) * N; } vec3 refract(vec3 I, vec3 N, float eta) { float cosi = dot(I, N); float sin2_t = eta * eta * (1.0 - cosi * cosi); if (sin2_t > 1.0) { return vec3(0.0); // 全反射 } float cos_t = sqrt(1.0 - sin2_t); return eta * I - (eta * cosi + cos_t) * N; } ``` ### 2.2 矩阵变换 矩阵用于表示变换操作,如平移、旋转、缩放。 #### 矩阵变换Shader实现 ```glsl // 平移矩阵 mat4 translate(vec3 translation) { return mat4( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, translation.x, translation.y, translation.z, 1.0 ); } // 缩放矩阵 mat4 scale(vec3 scale) { return mat4( scale.x, 0.0, 0.0, 0.0, 0.0, scale.y, 0.0, 0.0, 0.0, 0.0, scale.z, 0.0, 0.0, 0.0, 0.0, 1.0 ); } // 旋转矩阵(绕Y轴) mat4 rotateY(float angle) { float c = cos(angle); float s = sin(angle); return mat4( c, 0.0, s, 0.0, 0.0, 1.0, 0.0, 0.0, -s, 0.0, c, 0.0, 0.0, 0.0, 0.0, 1.0 ); } // 视图矩阵 mat4 lookAt(vec3 eye, vec3 center, vec3 up) { vec3 z_axis = normalize(eye - center); vec3 x_axis = normalize(cross(up, z_axis)); vec3 y_axis = cross(z_axis, x_axis); return mat4( x_axis.x, y_axis.x, z_axis.x, 0.0, x_axis.y, y_axis.y, z_axis.y, 0.0, x_axis.z, y_axis.z, z_axis.z, 0.0, -dot(x_axis, eye), -dot(y_axis, eye), -dot(z_axis, eye), 1.0 ); } // 投影矩阵 mat4 perspective(float fov, float aspect, float near, float far) { float tan_half_fov = tan(fov / 2.0); return mat4( 1.0 / (aspect * tan_half_fov), 0.0, 0.0, 0.0, 0.0, 1.0 / tan_half_fov, 0.0, 0.0, 0.0, 0.0, - (far + near) / (far - near), -1.0, 0.0, 0.0, - (2.0 * far * near) / (far - near), 0.0 ); } ``` ### 2.3 坐标系变换 图形学涉及多个坐标系,需要理解它们之间的转换关系。 #### 完整的坐标变换Shader ```glsl #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoord; out vec3 FragPos; out vec3 Normal; out vec2 TexCoord; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { // 模型矩阵变换(局部空间 -> 世界空间) vec4 worldPos = model * vec4(aPos, 1.0); FragPos = worldPos.xyz; // 法线矩阵(考虑非均匀缩放) mat3 normalMatrix = transpose(inverse(mat3(model))); Normal = normalize(normalMatrix * aNormal); // 纹理坐标传递 TexCoord = aTexCoord; // 完整的变换(局部空间 -> 裁剪空间) gl_Position = projection * view * worldPos; } ``` --- ## 第三章:光照模型 ### 3.1 Phong光照模型 Phong模型是经典的光照模型,包含环境光、漫反射和镜面反射三个分量。 ```glsl // Phong光照模型实现 struct Light { vec3 position; vec3 color; float intensity; }; struct Material { vec3 ambient; vec3 diffuse; vec3 specular; float shininess; }; vec3 calculatePhongLight(vec3 normal, vec3 fragPos, vec3 viewPos, Light light, Material material) { // 环境光分量 vec3 ambient = material.ambient * light.color * light.intensity * 0.1; // 漫反射分量 vec3 lightDir = normalize(light.position - fragPos); float diff = max(dot(normal, lightDir), 0.0); vec3 diffuse = material.diffuse * light.color * light.intensity * diff; // 镜面反射分量 vec3 viewDir = normalize(viewPos - fragPos); vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); vec3 specular = material.specular * light.color * light.intensity * spec; return ambient + diffuse + specular; } // 多光源光照计算 vec3 calculateMultipleLights(vec3 normal, vec3 fragPos, vec3 viewPos, Light lights[4], int lightCount, Material material) { vec3 result = vec3(0.0); for (int i = 0; i < lightCount; i++) { result += calculatePhongLight(normal, fragPos, viewPos, lights[i], material); } return result; } ``` ### 3.2 Blinn-Phong光照模型 Blinn-Phong模型是Phong模型的改进版本,使用半程向量代替反射向量,性能更好。 ```glsl // Blinn-Phong光照模型 vec3 calculateBlinnPhong(vec3 normal, vec3 fragPos, vec3 viewPos, Light light, Material material) { // 环境光 vec3 ambient = material.ambient * light.color * light.intensity * 0.1; // 漫反射 vec3 lightDir = normalize(light.position - fragPos); float diff = max(dot(normal, lightDir), 0.0); vec3 diffuse = material.diffuse * light.color * light.intensity * diff; // 镜面反射(使用半程向量) vec3 viewDir = normalize(viewPos - fragPos); vec3 halfwayDir = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfwayDir), 0.0), material.shininess); vec3 specular = material.specular * light.color * light.intensity * spec; return ambient + diffuse + specular; } ``` ### 3.3 点光源和聚光灯 ```glsl // 点光源衰减计算 float calculateAttenuation(float distance, float constant, float linear, float quadratic) { return 1.0 / (constant + linear * distance + quadratic * distance * distance); } // 聚光灯计算 float calculateSpotlight(vec3 lightPos, vec3 fragPos, vec3 spotDir, float cutOff, float outerCutOff) { vec3 lightDir = normalize(lightPos - fragPos); float theta = dot(lightDir, normalize(-spotDir)); if (theta > cutOff) { // 内部锥体,柔和边缘 float epsilon = cutOff - outerCutOff; float intensity = clamp((theta - outerCutOff) / epsilon, 0.0, 1.0); return intensity; } else { return 0.0; } } // 完整的点光源+聚光灯实现 vec3 calculateSpotLight(vec3 normal, vec3 fragPos, vec3 viewPos, vec3 lightPos, vec3 lightDir, vec3 lightColor, float lightIntensity, float cutOff, float outerCutOff, Material material) { vec3 ambient = material.ambient * lightColor * 0.1; vec3 L = normalize(lightPos - fragPos); float distance = length(lightPos - fragPos); float attenuation = 1.0 / (1.0 + 0.09 * distance + 0.032 * distance * distance); float diff = max(dot(normal, L), 0.0); vec3 diffuse = material.diffuse * lightColor * lightIntensity * diff * attenuation; vec3 V = normalize(viewPos - fragPos); vec3 H = normalize(L + V); float spec = pow(max(dot(normal, H), 0.0), material.shininess); vec3 specular = material.specular * lightColor * lightIntensity * spec * attenuation; float spotlightEffect = calculateSpotlight(lightPos, fragPos, lightDir, cutOff, outerCutOff); return (ambient + diffuse + specular) * spotlightEffect; } ``` --- ## 第四章:纹理与材质 ### 4.1 纹理采样 纹理是给物体添加细节的重要手段。 ```glsl // 基础纹理采样 uniform sampler2D texture1; in vec2 TexCoord; void main() { vec4 texColor = texture(texture1, TexCoord); FragColor = texColor; } // 多纹理混合 uniform sampler2D texture1; uniform sampler2D texture2; uniform float mixRatio; in vec2 TexCoord; void main() { vec4 color1 = texture(texture1, TexCoord); vec4 color2 = texture(texture2, TexCoord); FragColor = mix(color1, color2, mixRatio); } // 纹理坐标变换 uniform sampler2D texture; uniform float texScale; uniform float texRotation; uniform vec2 texOffset; in vec2 TexCoord; void main() { // 缩放 vec2 scaledCoord = TexCoord * texScale; // 旋转 float cosR = cos(texRotation); float sinR = sin(texRotation); mat2 rotationMatrix = mat2(cosR, -sinR, sinR, cosR); vec2 rotatedCoord = rotationMatrix * (scaledCoord - 0.5) + 0.5; // 偏移 vec2 finalCoord = rotatedCoord + texOffset; FragColor = texture(texture, finalCoord); } ``` ### 4.2 法线贴图 法线贴图用于模拟表面细节,而不增加几何复杂度。 ```glsl // 法线贴图实现 uniform sampler2D normalMap; uniform sampler2D diffuseMap; uniform float normalStrength; in vec2 TexCoord; in vec3 TangentLightPos; in vec3 TangentViewPos; in vec3 TangentFragPos; void main() { // 采样法线贴图 vec3 normal = texture(normalMap, TexCoord).rgb; // 将法线从[0,1]转换为[-1,1] normal = normalize(normal * 2.0 - 1.0); // 应用法线强度 normal = normalize(vec3(normal.xy * normalStrength, normal.z)); // 在切线空间计算光照 vec3 lightDir = normalize(TangentLightPos - TangentFragPos); vec3 viewDir = normalize(TangentViewPos - TangentFragPos); // 漫反射 float diff = max(dot(normal, lightDir), 0.0); vec3 diffuse = texture(diffuseMap, TexCoord).rgb * diff; // 镜面反射 vec3 halfwayDir = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0); vec3 specular = vec3(0.5) * spec; FragColor = vec4(diffuse + specular, 1.0); } ``` ### 4.3 高度贴图与视差贴图 ```glsl // 视差贴图实现 uniform sampler2D heightMap; uniform float heightScale; in vec2 TexCoord; in vec3 viewDirTS; // 切线空间中的视图方向 vec2 parallaxMapping(vec2 texCoords, vec3 viewDir) { float height = texture(heightMap, texCoords).r; vec2 p = viewDir.xy / viewDir.z * (height * heightScale); return texCoords - p; } void main() { vec2 texCoords = parallaxMapping(TexCoord, viewDirTS); FragColor = texture(diffuseMap, texCoords); } // 自适应视差贴图(更高质量) vec2 parallaxOcclusionMapping(vec2 texCoords, vec3 viewDir) { const float minLayers = 8.0; const float maxLayers = 32.0; float numLayers = mix(maxLayers, minLayers, max(dot(vec3(0.0, 0.0, 1.0), viewDir), 0.0)); float layerDepth = 1.0 / numLayers; float currentLayerDepth = 0.0; vec2 P = viewDir.xy * heightScale; vec2 deltaTexCoords = P / numLayers; vec2 currentTexCoords = texCoords; float currentDepthMapValue = texture(heightMap, currentTexCoords).r; while (currentLayerDepth < currentDepthMapValue) { currentTexCoords -= deltaTexCoords; currentDepthMapValue = texture(heightMap, currentTexCoords).r; currentLayerDepth += layerDepth; } // 深度值插值 vec2 prevTexCoords = currentTexCoords + deltaTexCoords; float afterDepth = currentDepthMapValue - currentLayerDepth; float beforeDepth = texture(heightMap, prevTexCoords).r - currentLayerDepth + layerDepth; float weight = afterDepth / (afterDepth - beforeDepth); vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); return finalTexCoords; } ``` --- ## 第五章:高级光照技术 ### 5.1 阴影映射 阴影映射是最常用的阴影技术。 ```glsl // 阴影映射的片段着色器 #version 330 core out vec4 FragColor; in vec2 TexCoord; in vec3 FragPos; in vec3 Normal; uniform sampler2D diffuseTexture; uniform sampler2D shadowMap; uniform vec3 lightPos; uniform vec3 viewPos; uniform vec3 lightColor; float calculateShadow(vec4 fragPosLightSpace) { // 执行透视除法 vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; // 转换到[0,1]范围 projCoords = projCoords * 0.5 + 0.5; // 最近的深度值 float closestDepth = texture(shadowMap, projCoords.xy).r; // 当前片段的深度值 float currentDepth = projCoords.z; // 检查当前片段是否在阴影中 float shadow = currentDepth > closestDepth + 0.005 ? 1.0 : 0.0; return shadow; } void main() { vec3 normal = normalize(Normal); vec3 color = texture(diffuseTexture, TexCoord).rgb; // 环境光 vec3 ambient = 0.1 * color; // 漫反射 vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(normal, lightDir), 0.0); vec3 diffuse = diff * color; // 镜面反射 vec3 viewDir = normalize(viewPos - FragPos); vec3 halfwayDir = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0); vec3 specular = vec3(0.2) * spec; // 阴影计算 float shadow = calculateShadow(fragPosLightSpace); vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * lightColor; FragColor = vec4(lighting, 1.0); } ``` ### 5.2 软阴影与PCF ```glsl // PCF(Percentage-Closer Filtering)软阴影 float calculateShadowPCF(vec4 fragPosLightSpace) { vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords = projCoords * 0.5 + 0.5; float currentDepth = projCoords.z; float shadow = 0.0; vec2 texelSize = 1.0 / textureSize(shadowMap, 0); // 3x3采样 for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r; shadow += currentDepth > pcfDepth + 0.005 ? 1.0 : 0.0; } } shadow /= 9.0; return shadow; } // PCSS(Percentage-Closer Soft Shadows)更高质量的软阴影 float calculateShadowPCSS(vec4 fragPosLightSpace) { vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords = projCoords * 0.5 + 0.5; float currentDepth = projCoords.z; float shadow = 0.0; // 查找遮挡物的平均深度 float blockerDepth = 0.0; float blockerCount = 0.0; vec2 texelSize = 1.0 / textureSize(shadowMap, 0); for (int x = -4; x <= 4; ++x) { for (int y = -4; y <= 4; ++y) { float depth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r; if (depth < currentDepth) { blockerDepth += depth; blockerCount += 1.0; } } } if (blockerCount > 0.0) { blockerDepth /= blockerCount; // 根据遮挡物距离调整滤波半径 float penumbra = (currentDepth - blockerDepth) / blockerDepth; float filterSize = penumbra * 0.1; // 调整参数 // 在调整后的半径内进行PCF int samples = int(9.0 + penumbra * 20.0); for (int x = -samples; x <= samples; ++x) { for (int y = -samples; y <= samples; ++y) { float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize * filterSize).r; shadow += currentDepth > pcfDepth + 0.005 ? 1.0 : 0.0; } } shadow /= float((samples * 2 + 1) * (samples * 2 + 1)); } return shadow; } ``` ### 5.3 环境光遮蔽(AO) ```glsl // SSAO(屏幕空间环境光遮蔽) #define KERNEL_SIZE 64 #define SAMPLE_RADIUS 0.5 #define BIAS 0.025 uniform sampler2D gPosition; uniform sampler2D gNormal; uniform sampler2D noiseTexture; uniform vec3 samples[KERNEL_SIZE]; uniform mat4 projection; in vec2 TexCoord; out float FragColor; void main() { vec3 fragPos = texture(gPosition, TexCoord).xyz; vec3 normal = texture(gNormal, TexCoord).rgb; vec3 randomVec = texture(noiseTexture, TexCoord).xyz; vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal)); vec3 bitangent = cross(normal, tangent); mat3 TBN = mat3(tangent, bitangent, normal); float occlusion = 0.0; for (int i = 0; i < KERNEL_SIZE; ++i) { vec3 sample = TBN * samples[i]; sample = fragPos + sample * SAMPLE_RADIUS; vec4 offset = vec4(sample, 1.0); offset = projection * offset; offset.xyz /= offset.w; offset.xyz = offset.xyz * 0.5 + 0.5; float sampleDepth = texture(gPosition, offset.xy).z; float rangeCheck = smoothstep(0.0, 1.0, SAMPLE_RADIUS / abs(fragPos.z - sampleDepth)); occlusion += (sampleDepth >= sample.z + BIAS ? 1.0 : 0.0) * rangeCheck; } occlusion = 1.0 - (occlusion / KERNEL_SIZE); FragColor = occlusion; } ``` --- ## 第六章:后期处理 ### 6.1 泛光效果 ```glsl // 高斯模糊 uniform sampler2D image; uniform bool horizontal; uniform float weight[5] = float[](0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162); in vec2 TexCoord; out vec4 FragColor; void main() { vec2 tex_offset = 1.0 / textureSize(image, 0); vec3 result = texture(image, TexCoord).rgb * weight[0]; if (horizontal) { for (int i = 1; i < 5; ++i) { result += texture(image, TexCoord + vec2(tex_offset.x * i, 0.0)).rgb * weight[i]; result += texture(image, TexCoord - vec2(tex_offset.x * i, 0.0)).rgb * weight[i]; } } else { for (int i = 1; i < 5; ++i) { result += texture(image, TexCoord + vec2(0.0, tex_offset.y * i)).rgb * weight[i]; result += texture(image, TexCoord - vec2(0.0, tex_offset.y * i)).rgb * weight[i]; } } FragColor = vec4(result, 1.0); } // 泛光组合 uniform sampler2D scene; uniform sampler2D bloomBlur; uniform float exposure; in vec2 TexCoord; out vec4 FragColor; void main() { vec3 hdrColor = texture(scene, TexCoord).rgb; vec3 bloomColor = texture(bloomBlur, TexCoord).rgb; // 色调映射 vec3 result = hdrColor + bloomColor; result = vec3(1.0) - exp(-result * exposure); result = pow(result, vec3(1.0 / 2.2)); // Gamma校正 FragColor = vec4(result, 1.0); } ``` ### 6.2 色调映射 ```glsl // Reinhard色调映射 vec3 reinhard(vec3 hdrColor) { return hdrColor / (hdrColor + vec3(1.0)); } // Filmic色调映射 vec3 filmic(vec3 hdrColor) { vec3 x = max(vec3(0.0), hdrColor - 0.004); return (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06); } // ACES色调映射 vec3 aces(vec3 hdrColor) { const float a = 2.51; const float b = 0.03; const float c = 2.43; const float d = 0.59; const float e = 0.14; return clamp((hdrColor * (a * hdrColor + b)) / (hdrColor * (c * hdrColor + d) + e), 0.0, 1.0); } // 暴露调整 uniform float exposure; vec3 adjustExposure(vec3 color) { return color * exposure; } // 白平衡 uniform vec3 whiteBalance; vec3 applyWhiteBalance(vec3 color) { vec3 balancedColor = color * whiteBalance; return balancedColor / dot(balancedColor, vec3(0.2126, 0.7152, 0.0722)) * dot(color, vec3(0.2126, 0.7152, 0.0722)); } ``` ### 6.3 屏幕空间反射(SSR) ```glsl // 屏幕空间反射实现 uniform sampler2D sceneColor; uniform sampler2D sceneDepth; uniform sampler2D sceneNormal; uniform mat4 projection; uniform mat4 inverseProjection; uniform mat4 view; uniform mat4 inverseView; in vec2 TexCoord; out vec4 FragColor; vec3 ssr(vec2 texCoord) { vec4 worldPos = reconstructWorldPos(TexCoord); vec3 worldNormal = texture(sceneNormal, TexCoord).rgb; vec3 worldViewDir = normalize(cameraPos - worldPos.xyz); vec3 worldReflectDir = reflect(-worldViewDir, worldNormal); // 在屏幕空间进行射线步进 vec3 rayStart = worldPos.xyz; vec3 rayDir = worldReflectDir; for (int i = 0; i < 32; i++) { vec3 rayPos = rayStart + rayDir * float(i) * 0.1; vec4 screenPos = projection * view * vec4(rayPos, 1.0); screenPos.xyz /= screenPos.w; screenPos.xyz = screenPos.xyz * 0.5 + 0.5; if (screenPos.x < 0.0 || screenPos.x > 1.0 || screenPos.y < 0.0 || screenPos.y > 1.0) { break; } float sceneDepth = texture(sceneDepth, screenPos.xy).r; float rayDepth = screenPos.z; if (abs(sceneDepth - rayDepth) < 0.01) { return texture(sceneColor, screenPos.xy).rgb; } } return vec3(0.0); } vec4 reconstructWorldPos(vec2 texCoord) { float depth = texture(sceneDepth, texCoord).r; vec4 clipPos = vec4(texCoord * 2.0 - 1.0, depth, 1.0); vec4 worldPos = inverseView * inverseProjection * clipPos; return worldPos / worldPos.w; } void main() { vec3 color = texture(sceneColor, TexCoord).rgb; vec3 reflection = ssr(TexCoord); // 根据入射角度混合反射 vec3 worldNormal = texture(sceneNormal, TexCoord).rgb; vec3 worldPos = reconstructWorldPos(TexCoord).xyz; vec3 viewDir = normalize(cameraPos - worldPos); float fresnel = pow(1.0 - max(dot(worldNormal, viewDir), 0.0), 3.0); FragColor = vec4(mix(color, reflection, fresnel), 1.0); } ``` --- ## 第七章:粒子系统 ### 7.1 GPU粒子系统 ```glsl // 粒子更新计算着色器 #version 430 core layout (local_size_x = 128) in; struct Particle { vec3 position; vec3 velocity; float life; float size; }; layout (std430, binding = 0) buffer ParticleBuffer { Particle particles[]; }; uniform float deltaTime; uniform vec3 gravity; uniform vec3 emitterPosition; uniform vec3 emitterVelocity; uniform float emitterRate; void main() { uint gid = gl_GlobalInvocationID.x; if (gid >= particles.length()) return; Particle p = particles[gid]; // 更新粒子 p.velocity += gravity * deltaTime; p.position += p.velocity * deltaTime; p.life -= deltaTime; // 重置死掉的粒子 if (p.life <= 0.0) { p.position = emitterPosition; p.velocity = emitterVelocity + vec3( (fract(sin(gid * 12.9898) * 43758.5453) - 0.5) * 2.0, (fract(cos(gid * 78.233) * 43758.5453) - 0.5) * 2.0, (fract(sin(gid * 37.719) * 43758.5453) - 0.5) * 2.0 ) * 5.0; p.life = 2.0 + fract(sin(gid * 27.1) * 43758.5453) * 2.0; p.size = 0.1 + fract(sin(gid * 9.1) * 43758.5453) * 0.2; } particles[gid] = p; } // 粒子渲染顶点着色器 #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in float aLife; layout (location = 2) in float aSize; out float Life; out vec2 TexCoord; uniform mat4 view; uniform mat4 projection; void main() { Life = aLife; // 计算屏幕位置 vec3 viewPos = view * vec4(aPos, 1.0)).xyz; vec4 clipPos = projection * vec4(viewPos, 1.0); // 应用粒子大小 gl_Position = clipPos; gl_PointSize = aSize * (1000.0 / -viewPos.z); } // 粒子渲染片段着色器 #version 330 core in float Life; out vec4 FragColor; uniform sampler2D particleTexture; void main() { // 圆形粒子 vec2 center = gl_PointCoord - 0.5; float dist = length(center); if (dist > 0.5) discard; // 根据生命周期淡出 float alpha = 1.0 - (Life / 2.0); alpha *= smoothstep(0.5, 0.0, dist); vec4 texColor = texture(particleTexture, gl_PointCoord); FragColor = texColor * alpha; } ``` ### 7.2 流体模拟 ```glsl // 2D流体模拟(Navier-Stokes方程) uniform sampler2D velocityField; uniform sampler2D pressureField; uniform sampler2D dyeField; uniform float dt; uniform float viscosity; uniform float diffusion; in vec2 TexCoord; out vec4 FragColor; // 获取相邻像素 vec4 getNeighbor(sampler2D tex, vec2 coord, vec2 offset) { return texture(tex, coord + offset * vec2(1.0 / textureSize(tex, 0))); } // 平散 vec4 diffuse(sampler2D tex, float diff, float dt) { vec4 result = texture(tex, TexCoord); vec4 left = getNeighbor(tex, TexCoord, vec2(-1, 0)); vec4 right = getNeighbor(tex, TexCoord, vec2(1, 0)); vec4 up = getNeighbor(tex, TexCoord, vec2(0, 1)); vec4 down = getNeighbor(tex, TexCoord, vec2(0, -1)); float a = dt * diff * 4.0; result = (result + a * (left + right + up + down)) / (1.0 + 4.0 * a); return result; } // 平流 vec4 advect(sampler2D tex, sampler2D vel, float dt) { vec2 coord = TexCoord - texture(vel, TexCoord).xy * dt * vec2(1.0 / textureSize(vel, 0)); return texture(tex, coord); } // 投影(不可压缩性) vec4 project(sampler2D vel, sampler2D pres) { vec4 result = texture(vel, TexCoord); vec4 leftP = getNeighbor(pres, TexCoord, vec2(-1, 0)); vec4 rightP = getNeighbor(pres, TexCoord, vec2(1, 0)); vec4 upP = getNeighbor(pres, TexCoord, vec2(0, 1)); vec4 downP = getNeighbor(pres, TexCoord, vec2(0, -1)); result -= vec4(rightP - leftP, downP - upP) * 0.5; return result; } void main() { // 完整的流体模拟步骤 vec4 velocity = texture(velocityField, TexCoord); vec4 pressure = texture(pressureField, TexCoord); vec4 dye = texture(dyeField, TexCoord); // 1. 扩散 velocity = diffuse(velocityField, viscosity, dt); dye = diffuse(dyeField, 0.001, dt); // 染料扩散较少 // 2. 平流 velocity = advect(velocityField, velocityField, dt); dye = advect(dyeField, velocityField, dt); // 3. 投影 velocity = project(velocityField, pressureField); FragColor = dye; } ``` --- ## 第八章:性能优化 ### 8.1 GPU Instancing ```cpp // 实例化渲染的C++实现 #include #include class InstancedMesh { private: unsigned int VAO, VBO, instanceVBO; unsigned int instanceCount; public: InstancedMesh(const std::vector& vertices, const std::vector& models) { instanceCount = models.size(); glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &instanceVBO); glBindVertexArray(VAO); // 顶点数据 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW); // 位置属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 法线属性 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); // 纹理坐标属性 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2); // 实例化矩阵属性 glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); glBufferData(GL_ARRAY_BUFFER, models.size() * sizeof(glm::mat4), models.data(), GL_STATIC_DRAW); // 每个mat4占用4个属性位置 for (int i = 0; i < 4; i++) { glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(i * sizeof(glm::vec4))); glEnableVertexAttribArray(3 + i); glVertexAttribDivisor(3 + i, 1); // 每实例更新 } glBindVertexArray(0); } void Draw() { glBindVertexArray(VAO); glDrawArraysInstanced(GL_TRIANGLES, 0, 36, instanceCount); glBindVertexArray(0); } }; // 实例化渲染着色器 #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoord; layout (location = 3) in mat4 aModel; // 实例化矩阵 out vec3 FragPos; out vec3 Normal; out vec2 TexCoord; uniform mat4 view; uniform mat4 projection; void main() { mat4 model = aModel; // 使用实例化矩阵 vec4 worldPos = model * vec4(aPos, 1.0); FragPos = worldPos.xyz; mat3 normalMatrix = transpose(inverse(mat3(model))); Normal = normalize(normalMatrix * aNormal); TexCoord = aTexCoord; gl_Position = projection * view * worldPos; } ``` ### 8.2 多线程渲染 ```cpp // 多线程渲染管线 #include #include #include class RenderThreadPool { private: std::vector workers; std::mutex mutex; std::vector taskQueue; bool shouldStop = false; public: RenderThreadPool(int numThreads) { for (int i = 0; i < numThreads; i++) { workers.emplace_back([this] { while (true) { RenderTask task; { std::lock_guard lock(mutex); if (shouldStop && taskQueue.empty()) { return; } if (!taskQueue.empty()) { task = taskQueue.back(); taskQueue.pop_back(); } else { continue; } } task.Execute(); } }); } } void AddTask(const RenderTask& task) { std::lock_guard lock(mutex); taskQueue.push_back(task); } void Wait() { while (true) { { std::lock_guard lock(mutex); if (taskQueue.empty()) break; } std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } ~RenderThreadPool() { shouldStop = true; for (auto& worker : workers) { worker.join(); } } }; ``` ### 8.3 Shader优化技巧 ```glsl // 优化1:避免分支 // 差的做法 float calculateSpecular(vec3 normal, vec3 lightDir, vec3 viewDir) { float specular = 0.0; if (dot(normal, lightDir) > 0.0) { vec3 reflectDir = reflect(-lightDir, normal); specular = pow(max(dot(viewDir, reflectDir), 0.0), 32.0); } return specular; } // 好的做法(使用step避免分支) float calculateSpecularOptimized(vec3 normal, vec3 lightDir, vec3 viewDir) { float NdotL = dot(normal, lightDir); float specularMask = step(0.0, NdotL); vec3 reflectDir = reflect(-lightDir, normal); float specular = pow(max(dot(viewDir, reflectDir), 0.0), 32.0); return specular * specularMask; } // 优化2:减少纹理采样 // 差的做法(重复采样) void main() { vec4 color1 = texture(diffuseMap, TexCoord); vec4 color2 = texture(normalMap, TexCoord); vec4 color3 = texture(roughnessMap, TexCoord); vec4 color4 = texture(aoMap, TexCoord); // ... } // 好的做法(一次采样多种属性) void main() { vec4 packedData = texture(packedTexture, TexCoord); vec3 diffuse = packedData.rgb; float roughness = packedData.a; // 解包其他纹理... } // 优化3:使用更快的数学函数 // 差的做法 float shadow = calculateExpensiveShadow(fragPosLightSpace); // 好的做法(使用预计算或简化算法) float shadow = texture(shadowMap, fragPosLightSpace.xy).r < fragPosLightSpace.z ? 1.0 : 0.0; // 优化4:减少计算精度 // 使用低精度变量 mediump vec3 color; lowp float alpha; // 优化5:避免循环展开 const int SAMPLE_COUNT = 16; for (int i = 0; i < SAMPLE_COUNT; i++) { // 固定次数循环,编译器可以优化 } // 优化6:使用共享常量 const float PI = 3.14159265359; const float INV_PI = 1.0 / PI; const float TWO_PI = 2.0 * PI; ``` --- ## 第九章:Ray Tracing与光线追踪 ### 9.1 基础Ray Tracing ```glsl // 简单的Ray Tracing片段着色器 #version 330 core out vec4 FragColor; in vec2 TexCoord; uniform vec3 cameraPos; uniform vec3 cameraDir; uniform vec3 cameraUp; struct Ray { vec3 origin; vec3 direction; }; struct Sphere { vec3 center; float radius; vec3 color; float reflectivity; }; struct HitInfo { bool hit; float distance; vec3 point; vec3 normal; vec3 color; float reflectivity; }; // 射线-球体相交检测 HitInfo intersectSphere(Ray ray, Sphere sphere) { HitInfo info; info.hit = false; vec3 oc = ray.origin - sphere.center; float a = dot(ray.direction, ray.direction); float b = 2.0 * dot(oc, ray.direction); float c = dot(oc, oc) - sphere.radius * sphere.radius; float discriminant = b * b - 4.0 * a * c; if (discriminant > 0.0) { float t = (-b - sqrt(discriminant)) / (2.0 * a); if (t > 0.001) { info.hit = true; info.distance = t; info.point = ray.origin + t * ray.direction; info.normal = normalize(info.point - sphere.center); info.color = sphere.color; info.reflectivity = sphere.reflectivity; } } return info; } // 追踪光线 HitInfo traceRay(Ray ray, Sphere[] spheres, int sphereCount) { HitInfo closestHit; closestHit.hit = false; closestHit.distance = 1e10; for (int i = 0; i < sphereCount; i++) { HitInfo hit = intersectSphere(ray, spheres[i]); if (hit.hit && hit.distance < closestHit.distance) { closestHit = hit; } } return closestHit; } // 计算颜色 vec3 calculateColor(Ray ray, Sphere[] spheres, int sphereCount, int depth) { HitInfo hit = traceRay(ray, spheres, sphereCount); if (!hit.hit) { // 天空背景 float t = 0.5 * (ray.direction.y + 1.0); return mix(vec3(1.0, 1.0, 1.0), vec3(0.5, 0.7, 1.0), t); } // 计算光照 vec3 lightPos = vec3(5.0, 5.0, 5.0); vec3 lightDir = normalize(lightPos - hit.point); vec3 lightColor = vec3(1.0, 1.0, 1.0); // 检查阴影 Ray shadowRay; shadowRay.origin = hit.point + hit.normal * 0.001; shadowRay.direction = lightDir; HitInfo shadowHit = traceRay(shadowRay, spheres, sphereCount); bool inShadow = shadowHit.hit && shadowHit.distance < length(lightPos - hit.point); if (inShadow) { lightColor = vec3(0.1); // 阴影 } // 漫反射 float diff = max(dot(hit.normal, lightDir), 0.0); vec3 diffuse = hit.color * lightColor * diff; // 镜面反射 vec3 viewDir = normalize(ray.origin - hit.point); vec3 reflectDir = reflect(-lightDir, hit.normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0); vec3 specular = vec3(1.0) * lightColor * spec; // 反射 vec3 reflection = vec3(0.0); if (depth > 0 && hit.reflectivity > 0.0) { Ray reflectRay; reflectRay.origin = hit.point + hit.normal * 0.001; reflectRay.direction = reflect(-ray.direction, hit.normal); reflection = calculateColor(reflectRay, spheres, sphereCount, depth - 1) * hit.reflectivity; } return diffuse + specular + reflection; } void main() { // 构建相机射线 vec3 cameraRight = normalize(cross(cameraDir, cameraUp)); vec3 cameraUpFixed = cross(cameraRight, cameraDir); float aspect = 1920.0 / 1080.0; vec2 uv = (TexCoord - 0.5) * vec2(aspect, 1.0); Ray ray; ray.origin = cameraPos; ray.direction = normalize(cameraDir + uv.x * cameraRight + uv.y * cameraUpFixed); // 场景定义 Sphere spheres[4]; spheres[0] = Sphere(vec3(0.0, 0.0, -3.0), 1.0, vec3(1.0, 0.0, 0.0), 0.5); spheres[1] = Sphere(vec3(2.0, 0.0, -4.0), 1.0, vec3(0.0, 1.0, 0.0), 0.5); spheres[2] = Sphere(vec3(-2.0, 0.0, -4.0), 1.0, vec3(0.0, 0.0, 1.0), 0.5); spheres[3] = Sphere(vec3(0.0, -1001.0, -3.0), 1000.0, vec3(0.5, 0.5, 0.5), 0.0); // 计算颜色 vec3 color = calculateColor(ray, spheres, 4, 3); // Gamma校正 color = pow(color, vec3(1.0 / 2.2)); FragColor = vec4(color, 1.0); } ``` ### 9.2 路径追踪 ```glsl // 路径追踪实现 #version 330 core out vec4 FragColor; in vec2 TexCoord; uniform sampler2D previousFrame; uniform int frameCount; // 随机数生成 float rand(vec2 co) { return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); } vec3 randomInHemisphere(vec3 normal) { vec2 uv = vec2(rand(TexCoord + frameCount), rand(TexCoord - frameCount)); float z = uv.x; float r = sqrt(1.0 - z * z); float phi = 2.0 * 3.14159265359 * uv.y; vec3 randomDir = vec3(r * cos(phi), r * sin(phi), z); // 确保在半球的正确一侧 if (dot(randomDir, normal) < 0.0) { randomDir = -randomDir; } return normalize(randomDir); } // 路径追踪主函数 vec3 pathTrace(Ray ray, Sphere[] spheres, int sphereCount, int depth) { if (depth <= 0) return vec3(0.0); HitInfo hit = traceRay(ray, spheres, sphereCount); if (!hit.hit) { // 天空盒 return vec3(0.0, 0.0, 0.0); } // 随机采样方向 vec3 randomDir = randomInHemisphere(hit.normal); Ray scatteredRay; scatteredRay.origin = hit.point + hit.normal * 0.001; scatteredRay.direction = randomDir; // 递归计算间接光照 vec3 indirectColor = pathTrace(scatteredRay, spheres, sphereCount, depth - 1); // 计算直接光照 vec3 lightPos = vec3(5.0, 5.0, 5.0); vec3 lightDir = normalize(lightPos - hit.point); float lightDist = length(lightPos - hit.point); Ray shadowRay; shadowRay.origin = hit.point + hit.normal * 0.001; shadowRay.direction = lightDir; HitInfo shadowHit = traceRay(shadowRay, spheres, sphereCount); bool inShadow = shadowHit.hit && shadowHit.distance < lightDist; vec3 directColor = inShadow ? vec3(0.0) : hit.color; // 组合直接和间接光照 return directColor + hit.color * indirectColor; } // 噪声过滤(时间累积) vec3 accumulateColor(vec3 newColor, vec3 oldColor, int frameCount) { return (oldColor * float(frameCount - 1) + newColor) / float(frameCount); } void main() { // 构建射线(与Ray Tracing相同) Ray ray = buildCameraRay(TexCoord); // 路径追踪 vec3 newColor = pathTrace(ray, spheres, 4, 8); // 时间累积 vec3 oldColor = texture(previousFrame, TexCoord).rgb; vec3 accumulatedColor = accumulateColor(newColor, oldColor, frameCount); // Gamma校正 vec3 finalColor = pow(accumulatedColor, vec3(1.0 / 2.2)); FragColor = vec4(finalColor, 1.0); } ``` --- ## 第十章:实战项目:完整的渲染引擎 ### 10.1 项目架构 ``` RenderingEngine/ ├── Core/ │ ├── Engine.cpp │ ├── Renderer.cpp │ ├── Shader.cpp │ └── Camera.cpp ├── Assets/ │ ├── ModelLoader.cpp │ ├── TextureLoader.cpp │ └── ShaderLoader.cpp ├── Rendering/ │ ├── ForwardRenderer.cpp │ ├── DeferredRenderer.cpp │ └── PostProcessor.cpp ├── Components/ │ ├── Transform.cpp │ ├── Material.cpp │ └── Light.cpp ├── Utils/ │ ├── MathUtils.cpp │ └── Profiler.cpp └── Shaders/ ├── Basic/ ├── PBR/ ├── PostProcessing/ └── RayTracing/ ``` ### 10.2 完整的PBR渲染系统 ```glsl // PBR材质定义 struct PBRMaterial { vec3 albedo; float metallic; float roughness; float ao; }; // PBR光照计算 vec3 calculatePBR(vec3 N, vec3 V, vec3 L, vec3 radiance, PBRMaterial mat) { vec3 H = normalize(V + L); // 漫反射项 vec3 F0 = mix(vec3(0.04), mat.albedo, mat.metallic); vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); vec3 kS = F; vec3 kD = (1.0 - kS) * (1.0 - mat.metallic); vec3 diffuse = kD * mat.albedo / PI; // 镜面反射项 float NDF = distributionGGX(N, H, mat.roughness); float G = geometrySmith(N, V, L, mat.roughness); vec3 specular = NDF * G * F / (4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001); // 最终颜色 float NdotL = max(dot(N, L), 0.0); return (diffuse + specular) * radiance * NdotL; } // 完整的PBR片段着色器 #version 330 core out vec4 FragColor; in vec2 TexCoord; in vec3 FragPos; in vec3 Normal; uniform PBRMaterial material; uniform vec3 camPos; uniform vec3 lightPositions[4]; uniform vec3 lightColors[4]; // 辅助函数 vec3 fresnelSchlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } float distributionGGX(vec3 N, vec3 H, float roughness) { float a = roughness * roughness; float a2 = a * a; float NdotH = max(dot(N, H), 0.0); float NdotH2 = NdotH * NdotH; float num = a2; float denom = (NdotH2 * (a2 - 1.0) + 1.0); denom = PI * denom * denom; return num / denom; } float geometrySchlickGGX(float NdotV, float roughness) { float r = (roughness + 1.0); float k = (r * r) / 8.0; float num = NdotV; float denom = NdotV * (1.0 - k) + k; return num / denom; } float geometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); float ggx1 = geometrySchlickGGX(NdotV, roughness); float ggx2 = geometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2; } void main() { vec3 N = normalize(Normal); vec3 V = normalize(camPos - FragPos); vec3 F0 = vec3(0.04); F0 = mix(F0, material.albedo, material.metallic); vec3 Lo = vec3(0.0); for (int i = 0; i < 4; i++) { vec3 L = normalize(lightPositions[i] - FragPos); vec3 H = normalize(V + L); float distance = length(lightPositions[i] - FragPos); float attenuation = 1.0 / (distance * distance); vec3 radiance = lightColors[i] * attenuation; vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); vec3 kS = F; vec3 kD = (1.0 - kS) * (1.0 - material.metallic); float NDF = distributionGGX(N, H, material.roughness); float G = geometrySmith(N, V, L, material.roughness); vec3 numerator = NDF * G * F; float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; vec3 specular = numerator / denominator; float NdotL = max(dot(N, L), 0.0); Lo += (kD * material.albedo / PI + specular) * radiance * NdotL; } vec3 ambient = vec3(0.03) * material.albedo * material.ao; vec3 color = ambient + Lo; color = color / (color + vec3(1.0)); color = pow(color, vec3(1.0 / 2.2)); FragColor = vec4(color, 1.0); } ``` ### 10.3 性能分析与优化 ```cpp // 性能分析工具 class Profiler { private: std::map> timings; std::chrono::time_point startTime; public: void StartTimer(const std::string& name) { startTime = std::chrono::high_resolution_clock::now(); } void EndTimer(const std::string& name) { auto endTime = std::chrono::high_resolution_clock::now(); float duration = std::chrono::duration(endTime - startTime).count(); timings[name].push_back(duration); // 保留最近100次记录 if (timings[name].size() > 100) { timings[name].erase(timings[name].begin()); } } float GetAverageTime(const std::string& name) { if (timings[name].empty()) return 0.0f; float sum = 0.0f; for (float time : timings[name]) { sum += time; } return sum / timings[name].size(); } void PrintReport() { for (auto& [name, times] : timings) { float avgTime = GetAverageTime(name); float maxTime = *std::max_element(times.begin(), times.end()); float minTime = *std::min_element(times.begin(), times.end()); std::cout << name << ":" << std::endl; std::cout << " Average: " << avgTime << "ms" << std::endl; std::cout << " Max: " << maxTime << "ms" << std::endl; std::cout << " Min: " << minTime << "ms" << std::endl; } } }; // 使用示例 Profiler profiler; void RenderScene() { profiler.StartTimer("Culling"); CullObjects(); profiler.EndTimer("Culling"); profiler.StartTimer("Shadows"); RenderShadows(); profiler.EndTimer("Shadows"); profiler.StartTimer("Geometry"); RenderGeometry(); profiler.EndTimer("Geometry"); profiler.StartTimer("PostProcessing"); RenderPostProcessing(); profiler.EndTimer("PostProcessing"); profiler.PrintReport(); } ``` --- ## 结语 图形学是一门博大精深的学科,本文涵盖了从基础概念到高级技术的各个方面。要真正精通图形学,需要: 1. **扎实的数学基础**:向量、矩阵、微积分是图形学的基础 2. **深入的编程实践**:动手编写Shader,理解GPU工作原理 3. **持续的实验探索**:尝试不同的技术,理解其原理和应用场景 4. **关注最新发展**:图形学发展迅速,需要不断学习新技术 记住,图形学不仅是技术,更是艺术。通过编程创造美丽的视觉效果,是图形学最大的魅力所在。希望你能在图形学的道路上不断前进,创造出令人惊叹的作品!
所属分类:
评论 0

发表评论 取消回复

Shift+Enter 换行  ·  Enter 发送
还没有评论,来发表第一条吧