gl_LightSource[n].position

本文介绍如何使用OpenGL的Shader编程实现镜面光照效果,并通过具体的Vertex Shader代码展示镜面光照的计算过程。文中还对比了固定功能管线和可编程管线在设置光源位置上的不同做法。

如果你还是想使用固定功能管线的glLight*来设置光源的位置的话,需要改一下shader代码。GLSL提供了一个内置的变量gl_LightSource[n].position 其中n为第几个光源。改一下上面的shader.

#define FIX_FUNCTION 1
char vsChar[] = {  "#version 120\n"
  "uniform vec3 lightPos;\n"
  "void main(void)"
  "{"
  "  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"
  "  vec3 N = normalize(gl_NormalMatrix * gl_Normal);"
  "  vec4 V = gl_ModelViewMatrix * gl_Vertex;"
  #if FIX_FUNCTION  
  " vec3 L = normalize(gl_LightSource[0].position.xyz - V.xyz);"
  #else
  "  vec3 L = normalize(lightPos - V.xyz);"
  #endif
  "  float NdotL = dot(N, L);"
  "  gl_FrontColor = gl_Color * vec4(max(0.0, NdotL));"
  "}"};
  
  void SetupRC()
{
...
#if FIX_FUNCTION
  glLightfv(GL_LIGHT0, GL_POSITION, g_lightPos);
  #else
  lightPosLocation = glGetUniformLocation(program, "lightPos");
    if (lightPosLocation != -1)
  {
    glUniform3fv(lightPosLocation, 1, g_lightPos);
  }
  #endif
  }

这样效果是等价的。你会发现我并没有调用

glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);

来开启光照。这就是shader的好处,不需要这一堆的开关指令。需要用到的就拿去用吧,没用到就相当于关闭了。shader 可编程管线更加灵活。

镜面光照

镜面光照要考虑光的入射方向,以及眼睛所在的位置。由顶点指向光源的向量,顶点的法线,顶点指向照相机的向量就可以决定镜面光在该顶点的强度。简单起见默认默认照相机在z的正方向上,假设顶点到照相机的单位向量为(0.0, 0.0, 1.0)。根据镜面光的公式:

Cspec = max{N • H, 0}Sexp * Cmat * Cli

image

H是光线向量与视角向量之间夹角正中方向的单位向量。Sexp代表镜面指数,用于控制镜面光照的紧聚程度。Cmat是材料的颜色,Cli是光的颜色。Cspec 是最终求得的镜面颜色。在下面简单的例子中,假设光是白光(1.0, 1.0, 1.0, 1.0),镜面材料的镜面光属性也为(1.0, 1.0, 1.0, 1.0),所以我们可以忽略掉这一项乘的操作。其中N,L,Cmat 和 Cli 和散射光是一样的。这里镜面指数固定为128.

编写如下的specular.vs:

#version 120
uniform vec3 lightPos;
void main(void)
{
//MVP transform
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
  //caculate diffuse
  //normal
  vec3 N = normalize(gl_NormalMatrix * gl_Normal);
  //transform to view coordinate
  vec4 V = (gl_ModelViewMatrix * gl_Vertex);
  //light vector
  vec3 L = normalize(lightPos - V.xyz);
  float NdotL = dot(N,L);
  vec4 diffuse = vec4(max(NdotL, 0.0)) * gl_Color;
  //specular
  vec3 H = normalize(vec3(0.00.01.0) + L);
  float NdotH = max(0.0, dot(N,H));
  const float expose = 128.0;
  vec4 specular = vec4(0.0);
  if (NdotL > 0.0)
  specular = vec4(pow(NdotH, expose));

  gl_FrontColor = diffuse + specular;
}

效果图:

image

提升镜面光照
#include <glad/glad.h> #include <GLFW/glfw3.h> #include <glad/stb_image.h> // 仅用于加载纹理,轻量级库 #include <iostream> #include <cmath> #define _USE_MATH_DEFINES // 窗口设置 const unsigned int SCR_WIDTH = 800; const unsigned int SCR_HEIGHT = 600; // 全局状态 enum ShadingModel { FLAT, GOURAUD, PHONG }; ShadingModel currentShading = PHONG; bool isTextured = true; float rotX = 0.0f, rotY = 0.0f; // 旋转角度 float scaleFactor = 1.0f; // 缩放因子 float translateX = 0.0f, translateY = 0.0f; // 平移 bool firstMouse = true; float lastX = SCR_WIDTH / 2.0f; float lastY = SCR_HEIGHT / 2.0f; float cameraZ = 3.0f; // 相机Z轴位置(控制缩放) // 基础矩阵运算(替代glm) struct Mat4 { float m[4][4] = {0}; Mat4() { // 单位矩阵 m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f; } }; // 矩阵乘法 Mat4 multiplyMat4(const Mat4& a, const Mat4& b) { Mat4 res; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { res.m[i][j] = 0; for (int k = 0; k < 4; k++) { res.m[i][j] += a.m[i][k] * b.m[k][j]; } } } return res; } // 平移矩阵 Mat4 translate(float x, float y, float z) { Mat4 res; res.m[3][0] = x; res.m[3][1] = y; res.m[3][2] = z; return res; } // X轴旋转矩阵 Mat4 rotateX(float rad) { Mat4 res; res.m[1][1] = cos(rad); res.m[1][2] = -sin(rad); res.m[2][1] = sin(rad); res.m[2][2] = cos(rad); return res; } // Y轴旋转矩阵 Mat4 rotateY(float rad) { Mat4 res; res.m[0][0] = cos(rad); res.m[0][2] = sin(rad); res.m[2][0] = -sin(rad); res.m[2][2] = cos(rad); return res; } // 缩放矩阵 Mat4 scale(float x, float y, float z) { Mat4 res; res.m[0][0] = x; res.m[1][1] = y; res.m[2][2] = z; return res; } // 透视投影矩阵 Mat4 perspective(float fovRad, float aspect, float near, float far) { Mat4 res; float tanHalfFov = tan(fovRad / 2.0f); res.m[0][0] = 1.0f / (aspect * tanHalfFov); res.m[1][1] = 1.0f / tanHalfFov; res.m[2][2] = -(far + near) / (far - near); res.m[2][3] = -1.0f; res.m[3][2] = -(2.0f * far * near) / (far - near); res.m[3][3] = 0.0f; return res; } // 着色器源码 const char* vertexShaderSource = R"( #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoord; uniform mat4 model; uniform mat4 view; uniform mat4 projection; uniform int shadingModel; out vec3 FragPos; out vec3 Normal; out vec2 TexCoord; out vec3 GouraudColor; struct Light { vec3 position; vec3 ambient; vec3 diffuse; vec3 specular; }; uniform Light light; uniform vec3 viewPos; vec3 calculateLighting(vec3 normal, vec3 fragPos) { vec3 ambient = light.ambient; vec3 lightDir = normalize(light.position - fragPos); float diff = max(dot(normal, lightDir), 0.0); vec3 diffuse = light.diffuse * diff; vec3 viewDir = normalize(viewPos - fragPos); vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); vec3 specular = light.specular * spec; return ambient + diffuse + specular; } void main() { FragPos = vec3(model * vec4(aPos, 1.0)); Normal = mat3(transpose(inverse(model))) * aNormal; TexCoord = aTexCoord; gl_Position = projection * view * vec4(FragPos, 1.0); if (shadingModel == 1) { // Gouraud着色 GouraudColor = calculateLighting(Normal, FragPos); } } )"; const char* fragmentShaderSource = R"( #version 330 core in vec3 FragPos; in vec3 Normal; in vec2 TexCoord; in vec3 GouraudColor; uniform int shadingModel; uniform bool isTextured; uniform sampler2D texture1; uniform vec3 objectColor; struct Light { vec3 position; vec3 ambient; vec3 diffuse; vec3 specular; }; uniform Light light; uniform vec3 viewPos; vec3 calculateLighting(vec3 normal, vec3 fragPos) { vec3 ambient = light.ambient; vec3 lightDir = normalize(light.position - fragPos); float diff = max(dot(normal, lightDir), 0.0); vec3 diffuse = light.diffuse * diff; vec3 viewDir = normalize(viewPos - fragPos); vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); vec3 specular = light.specular * spec; return ambient + diffuse + specular; } void main() { vec3 color = isTextured ? texture(texture1, TexCoord).rgb : objectColor; if (shadingModel == 0) { // 平面着色 vec3 normal = normalize(Normal); gl_FragColor = vec4(color * calculateLighting(normal, FragPos), 1.0); } else if (shadingModel == 1) { // Gouraud着色 gl_FragColor = vec4(color * GouraudColor, 1.0); } else { // Phong着色 vec3 normal = normalize(Normal); gl_FragColor = vec4(color * calculateLighting(normal, FragPos), 1.0); } } )"; // 回调函数 void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } void mouse_callback(GLFWwindow* window, double xpos, double ypos) { if (firstMouse) { lastX = xpos; lastY = ypos; firstMouse = false; return; } float xoffset = xpos - lastX; float yoffset = lastY - ypos; // Y轴反向 lastX = xpos; lastY = ypos; // 右键旋转 if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) { rotY += xoffset * 0.1f; rotX += yoffset * 0.1f; } // 中键平移 else if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS) { translateX += xoffset * 0.005f; translateY -= yoffset * 0.005f; } } void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { cameraZ = std::max(1.0f, std::min(cameraZ - yoffset * 0.2f, 10.0f)); // 滚轮控制相机距离 } void processInput(GLFWwindow* window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); // 数字键1-3切换着色模型 if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS) currentShading = FLAT; if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS) currentShading = GOURAUD; if (glfwGetKey(window, GLFW_KEY_3) == GLFW_PRESS) currentShading = PHONG; // T键切换纹理 if (glfwGetKey(window, GLFW_KEY_T) == GLFW_PRESS) isTextured = !isTextured; // 鼠标左键缩放物体 if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) { scale *= (1.0f + yoffset * 0.1f); scale = std::max(0.1f, std::min(scale, 5.0f)); } } // 加载纹理(仅使用stb_image,不依赖其他库) unsigned int loadTexture(const char* path) { unsigned int textureID; glGenTextures(1, &textureID); int width, height, nrChannels; stbi_set_flip_vertically_on_load(true); // 翻转纹理Y轴 unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 0); if (data) { GLenum format = (nrChannels == 4) ? GL_RGBA : GL_RGB; glBindTexture(GL_TEXTURE_2D, textureID); glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); // 设置纹理过滤和环绕方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { std::cout << "Failed to load texture: " << path << std::endl; } stbi_image_free(data); return textureID; } // 编译着色器工具函数 unsigned int compileShader(unsigned int type, const char* source) { unsigned int shader = glCreateShader(type); glShaderSource(shader, 1, &source, NULL); glCompileShader(shader); // 检查错误 int success; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { char infoLog[512]; glGetShaderInfoLog(shader, 512, NULL, infoLog); std::cout << "Shader compilation error:\n" << infoLog << std::endl; } return shader; } 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(SCR_WIDTH, SCR_HEIGHT, "OpenGL Demo (Only GLAD+GLFW)", NULL, NULL); if (!window) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glfwSetCursorPosCallback(window, mouse_callback); glfwSetScrollCallback(window, scroll_callback); // 初始化GLAD if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } // 启用深度测试 glEnable(GL_DEPTH_TEST); // 编译着色器 unsigned int vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource); unsigned int fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource); unsigned int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); // 立方体顶点数据(位置、法向量、纹理坐标) float vertices[] = { // 前后面 -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左右面 -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // 上下底面 -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f }; // 创建VAO、VBO unsigned int VAO, VBO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 顶点属性:位置(0)、法向量(1)、纹理坐标(2) 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); // 加载纹理(准备一张名为texture.jpg的图片放在同级目录) unsigned int texture = loadTexture("texture.jpg"); glUseProgram(shaderProgram); glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0); // 设置光照参数 glUniform3f(glGetUniformLocation(shaderProgram, "light.position"), 1.2f, 1.0f, 2.0f); glUniform3f(glGetUniformLocation(shaderProgram, "light.ambient"), 0.2f, 0.2f, 0.2f); glUniform3f(glGetUniformLocation(shaderProgram, "light.diffuse"), 0.8f, 0.8f, 0.8f); glUniform3f(glGetUniformLocation(shaderProgram, "light.specular"), 1.0f, 1.0f, 1.0f); glUniform3f(glGetUniformLocation(shaderProgram, "objectColor"), 0.8f, 0.5f, 0.2f); glUniform3f(glGetUniformLocation(shaderProgram, "viewPos"), 0.0f, 0.0f, cameraZ); // 渲染循环 while (!glfwWindowShouldClose(window)) { processInput(window); // 清空缓冲 glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 使用着色器 glUseProgram(shaderProgram); // 计算模型矩阵(平移→旋转→缩放) Mat4 model = translate(translateX, translateY, 0.0f); Mat4 rotX = rotateX(rotateX * M_PI / 180.0f); Mat4 rotY = rotateY(rotateY * M_PI / 180.0f); model = multiplyMat4(model, rotY); model = multiplyMat4(model, rotX); model = multiplyMat4(model, scale(scale, scale, scale)); // 计算视图矩阵(相机沿Z轴移动) Mat4 view = translate(0.0f, 0.0f, -cameraZ); // 计算投影矩阵 float aspect = (float)SCR_WIDTH / SCR_HEIGHT; Mat4 projection = perspective(45.0f * M_PI / 180.0f, aspect, 0.1f, 100.0f); // 传递矩阵到着色器 glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, &model.m[0][0]); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, &view.m[0][0]); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, &projection.m[0][0]); // 更新着色模型和纹理状态 glUniform1i(glGetUniformLocation(shaderProgram, "shadingModel"), currentShading); glUniform1i(glGetUniformLocation(shaderProgram, "isTextured"), isTextured); // 绑定纹理并绘制 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 36); // 交换缓冲和事件 glfwSwapBuffers(window); glfwPollEvents(); } // 清理资源 glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteProgram(shaderProgram); glfwTerminate(); return 0; }
最新发布
10-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值