1. 光照基本模型
在OpenGL ES 2.X中采用的光照模型(通道)分3种:环境光、散射光和镜面光。
1.1 环境光
环境光(Ambient)指的是从四面八方照射到物体上,全方位360°都均匀的光。
数学模型公式:
环境光照射结果 = 材质的反射系数 ∗ 环境光强度 环境光照射结果=材质的反射系数*环境光强度 环境光照射结果=材质的反射系数∗环境光强度
环境光下顶点着色器和片元着色器完整代码:
vertex.sh
uniform mat4 uMVPMatrix;//总变换矩阵
attribute vec3 aPosition;//顶点位置
varying vec3 vPosition;//传递给片元着色器的的顶点位置
varying vec4 vAmbient;//传递给片元着色器的环境光分量
void main(){
gl_Position = uMVPMatrix * vec4(aPosition,1);
vPosition = aPosition;
vAmbient = vec4(0.15,0.15,0.15,1.0);
}
frag.sh
precision mediump float;
uniform float uR;//球半径
varying vec3 vPosition;//顶点着色器传递过来的
varying vec4 vAmbient;//顶点着色器传递过来的环境光强度
void main(){
vec3 color;
float n = 8.0;
float span = 2.0*uR/n;
int i = int((vPosition.x + uR)/span);
int j = int((vPosition.y + uR)/span);
int k = int((vPosition.z + uR)/span);
int whichColor = int(mod(float(i+j+k),2.0));
if(whichColor==1){
color = vec3(0.678,0.231,0.129);
}else{
color = vec3(1.0,1.0,1.0);
}
vec4 finalColor = vec4(color,0);
gl_FragColor = finalColor * vAmbient;
}
【环境光说明】
- 环境光没有方向性;
- 环境光的照射效果与光源位置无关(看下面的GIF图);
- 实际开发中环境光强度一般设置较弱;
1.2 散射光
散射光(Diffuse),指的是从物体表面向全方位360°均匀反射的光。
数学模型公式:
散射光最终强度 = 散射光强度 ∗ m a x ( c o s ( 入射角 ) , 0 ) 散射光照射结果 = 材质的反射系数 ∗ 散射光最终强度 散射光最终强度=散射光强度*max(cos(入射角),0)\\ 散射光照射结果= 材质的反射系数*散射光最终强度 散射光最终强度=散射光强度∗max(cos(入射角),0)散射光照射结果=材质的反射系数∗散射光最终强度
- 材质的反射系数:物体被照射处颜色;
- 散射光强度:散射光中RGB3色彩通道强度
散射光下顶点着色器和片元着色器完整代码:
vertex.sh
uniform mat4 uMVPMatrix;
uniform mat4 uMMatrix;
uniform vec3 uLightLocation;
attribute vec3 aPosition;
attribute vec3 aNormal;
varying vec3 vPosition;
varying vec4 vDiffuse;//用于传递给片元着色器的散射光分量
void pointLight(in vec3 normal,inout vec4 diffuse,in vec3 lightLocation,in vec4 lightDiffuse){
vec3 normalTarget = aPosition + normal;
vec3 newNormal = (uMMatrix*vec4(normalTarget,1)).xyz - (uMMatrix*vec4(aPosition,1)).xyz;
newNormal = normalize(newNormal);
vec3 vp = normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);
vp = normalize(vp);
float nDotViewPosition=max(0.0,dot(newNormal,vp));
diffuse = lightDiffuse * nDotViewPosition;
}
void main(){
gl_Position = uMVPMatrix * vec4(aPosition,1);
vec4 diffuseTemp = vec4(0.0,0.0,0.0,0.0);
pointLight(normalize(aNormal),diffuseTemp,uLightLocation,vec4(0.8,0.8,0.8,1.0));
vDiffuse = diffuseTemp;
vPosition = aPosition;
}
frag.sh
precision mediump float;
uniform float uR;
varying vec3 vPosition;
varying vec4 vDiffuse;//接收从顶点传递过来的散射光最终强度
void main(){
vec3 color;
float n = 8.0;
float span = 2.0*uR/n;
int i = int((vPosition.x + uR)/span);
int j = int((vPosition.y + uR)/span);
int k = int((vPosition.z + uR)/span);
int whichColor = int(mod(float(i+j+k),2.0));
if(whichColor==1){
color = vec3(0.678,0.231,0.129);
}else{
color = vec3(1.0,1.0,1.0);
}
vec4 finalColor = vec4(color,0);
gl_FragColor = finalColor * vDiffuse;
}
【散射光说明】
- 散射光,也可称为漫反射;
- 反射后散射光在各个方向均匀;
- 散射光反射的强度与入射光的强度及入射角度密切相关,入射角越大,反射强度越弱(看下方GIF图);
1.3 镜面光
镜面光(Specular),光滑表面被照射时方向集中的反射光。
数学模型公式:
镜面光最终强度
=
镜面光强度
∗
m
a
x
(
0
,
(
c
o
s
(
半向量与法向量夹角
)
)
粗糙度
)
镜面光照射结果
=
材质的反射系数
∗
镜面光最终强度
镜面光最终强度=镜面光强度*max(0,(cos(半向量与法向量夹角))粗糙度)\\ 镜面光照射结果=材质的反射系数*镜面光最终强度
镜面光最终强度=镜面光强度∗max(0,(cos(半向量与法向量夹角))粗糙度)镜面光照射结果=材质的反射系数∗镜面光最终强度
镜面光下顶点着色器和片元着色器完整代码:
vertex.sh
uniform mat4 uMVPMatrix;
uniform mat4 uMMatrix;
uniform vec3 uLightLocation;
uniform vec3 uCamera;
attribute vec3 aPosition;
attribute vec3 aNormal;
varying vec3 vPosition;
varying vec4 vSpecular;
void pointLight(in vec3 normal,inout vec4 specular,in vec3 lightLocation,in vec4 lightSpecular){
vec3 normalTarget = aPosition + normal;
vec3 newNormal = (uMMatrix*vec4(normalTarget,1)).xyz - (uMMatrix*vec4(aPosition,1)).xyz;
newNormal = normalize(newNormal);
vec3 eye = normalize(uCamera-(uMMatrix*vec4(aPosition,1)).xyz);
vec3 vp = normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);
vp = normalize(vp);
vec3 halfVector = normalize(vp+eye);
float shininess = 50.0;
float nDotViewHalfVector=dot(newNormal,halfVector);
float powerFactor = max(0.0,pow(nDotViewHalfVector,shininess));
specular = lightSpecular*powerFactor;
}
void main(){
gl_Position = uMVPMatrix * vec4(aPosition,1);
vec4 specularTemp = vec4(0.0,0.0,0.0,0.0);
pointLight(normalize(aNormal),specularTemp,uLightLocation,vec4(0.7,0.7,0.7,1.0));
vSpecular = specularTemp;
vPosition = aPosition;
}
frag.sh
precision mediump float;
uniform float uR;
varying vec3 vPosition;
varying vec4 vSpecular;
void main(){
vec3 color;
float n = 8.0;
float span = 2.0*uR/n;
int i = int((vPosition.x + uR)/span);
int j = int((vPosition.y + uR)/span);
int k = int((vPosition.z + uR)/span);
int whichColor = int(mod(float(i+j+k),2.0));
if(whichColor==1){
color = vec3(0.678,0.231,0.129);
}else{
color = vec3(1.0,1.0,1.0);
}
vec4 finalColor = vec4(color,0);
gl_FragColor = finalColor * vSpecular;
}
【镜面光说明】
- 镜面光强度依赖入射光与被照射点法向量夹角、观察者位置;
- 粗糙度越小,镜面光面积越大
- 镜面光随光源位置的变化而变化
三种光混合后效果GIF
案例源码下载
【参考】
- https://www.khronos.org/opengles/
- 《OpenGL ES应用开发实践指南 Android卷 Kevin Brothaler》
- https://en.wikipedia.org/wiki/OpenGL_ES
- https://developer.android.google.cn/guide/topics/graphics/opengl
- https://developer.android.google.cn/training/graphics/opengl
- Android3D游戏开发技术宝典OpenGL ES 2.0