gl_LightSource[n].position

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果你还是想使用固定功能管线的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

提升镜面光照
### OpenGL 中的光照基础 在计算机图形学中,光照模拟是创建逼真图像的关键部分之一。OpenGL 提供了一套工具来实现这一点,允许开发者通过不同的方式定义光源及其属性。 #### 定义光源 为了设置光的位置和其他特性,在场景中可以配置多种类型的光源: - **方向光 (Directional Light)**: 这种光线来自无限远的地方,所有平行射线都具有相同的方向[^1]。 - **点光源(Point Light)**: 类似于灯泡的效果,从单一点发出并向四周扩散,强度随距离衰减。 - **聚光灯光源(Spotlight Source)**: 只照亮特定角度内的物体,类似于手电筒或舞台上的追光灯。 下面是一个简单的例子展示如何初始化一个方向光并将其应用到材质上: ```cpp // 设置当前颜色材料参数 glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialf(GL_FRONT, GL_SHININESS, shininess); // 启用光照计算 glEnable(GL_LIGHTING); // 初始化第一个光源为方向光 GLfloat light_direction[] = {0.0f, 0.0f,-1.0f}; glLightfv(GL_LIGHT0,GL_POSITION,light_direction); glEnable(GL_LIGHT0); ``` 这段代码设置了环境反射率、漫反射率以及镜面高光度等材质属性,并启用了默认的第一个光源`GL_LIGHT0`作为方向光使用。 #### 编写着色器以支持光照效果 现代OpenGL通常依赖可编程管线中的着色器来进行更复杂的渲染操作。对于光照而言,顶点着色器负责处理每顶点的颜色变化;而片元着色器则用于最终像素级别的色彩混合与调整[^2]。 这里给出一段基于Unity ShaderLab编写的简单顶点/片段程序示例,它实现了基本的Phong光照模型: ```c Shader "Custom/BasicPhong" { Properties { _Color ("Main Color", Color) = (1,1,1,1) _SpecColor ("Specular Material Color", Color) = (1,1,1,1) _Shininess ("Shininess", Float) = 10 } SubShader { Tags {"RenderType"="Opaque"} Pass { Name "FORWARD" CGPROGRAM #pragma vertex vert #pragma fragment frag struct appdata_t { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 diff : COLOR0; // Diffuse color float3 spec : COLOR1; // Specular color }; uniform float4 _LightPosWorldSpace; uniform float4 _Color; uniform float4 _SpecColor; uniform float _Shininess; v2f vert(appdata_t i){ v2f o; o.pos = mul(UNITY_MATRIX_MVP,i.vertex); // 计算法向量变换后的世界坐标系下的位置 float3 worldNormal = normalize(mul((float3x3)_Object2World,(i.normal))); // 获取入射光方向 float3 lightDir = normalize(_LightPosWorldSpace.xyz-_WorldSpaceCameraPos); // 求解漫反射分量 half NdotL = max(dot(worldNormal,lightDir),0.); o.diff.rgb = NdotL * _Color.rgb; // 高光计算 if(NdotL>0.) o.spec.rgb = pow(max(dot(reflect(-lightDir,worldNormal), normalize(o.pos-o.pos)),0.),_Shininess)*_SpecColor.rgb; return o; } fixed4 frag(v2f i):COLOR{ return fixed4(i.diff+i.spec,1); } ENDCG } } } ``` 此段代码展示了如何利用自定义属性控制对象外观的同时也考虑到了不同种类的光照贡献——即漫反射(`diff`)和镜面反射(`spec`)两者的组合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值