背景
随着对光照相关shader的学习,发现前面很多知识理解的并不正确。下面记录一下前段时间关于光照颜色插值的计算发现的问题。
片元中计算光照
先看一张效果图
这是我们使用片元着色器进行光照计算得出的效果。在这里我们对每个片元进行光照计算,也就是说每个点与光源的距离和角度我们都会计算,并为之赋一个准确值。于是就形成了上图的光照效果,这种方式可以比较精准地反应光照,但是计算量巨大,如果不对多光源进行特殊处理,很容易导致画面卡顿。
fragmentshader
precision mediump float;
struct LightProperties{
bool isEnabled;
bool isLocal;
bool isSpot;
vec3 ambient;
vec3 color;
vec3 position;
vec3 halfVector;
vec3 coneDirection;
float spotCosCutoff;
float spotExponent;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
};
const int MaxLights = 5;//用几个光源设置几个,开多了会卡
uniform LightProperties uLights[MaxLights];
uniform sampler2D sTexture;
varying vec2 vTextureCoord;
varying vec4 vglPositionRec;
varying vec3 vPointNormal;
varying vec3 uLightPosition;
void main(){
vec3 scatteredLight = vec3(0.0);
vec3 reflectedLight = vec3(0.0);
for(int light=0; light<MaxLights; light++){
if(! uLights[light].isEnabled){
continue;
}
//vec3 halfVector;
vec3 lightDirection = uLights[light].position;
float attenuation = 1.0;
if(uLights[light].isLocal){ //点光或者聚光
lightDirection = lightDirection - vec3(vglPositionRec);
float lightDistance = length(lightDirection);
lightDirection = lightDirection / lightDistance;
attenuation = 1.0/(uLights[light].constantAttenuation +
uLights[light].linearAttenuation*lightDistance );
if(uLights[light].isSpot){
float spotCos = dot(lightDirection, -uLights[light].coneDirection);
if(spotCos < uLights[light].spotCosCutoff)
attenuation = 0.0;
else
attenuation *= pow(spotCos, uLights[light].spotExponent);
}
float diffuse = max(0.0, dot(vPointNormal, lightDirection));
scatteredLight += (uLights[light].ambient + uLights[light].color * diffuse) * attenuation;
}
gl_FragColor = min(texture2D(sTexture, vTextureCoord) * vec4(scatteredLight, 1), vec4(1.0));
}
vertexshader
uniform mat4 uMVPMatrix;
uniform mat4 uMVMatrix;
attribute vec3 aPosition;
attribute vec2 aColor;
varying vec2 vTextureCoord;
varying vec4 vglPositionRec;//物体未经透视的位置
//传入点的法线
attribute vec3 aPointNormal;
varying vec3 vPointNormal;
//传入光源位置
attribute vec3 aLightPosition;
varying vec3 uLightPosition;
void main(){
vPointNormal = normalize(aPointNormal);
gl_Position = uMVPMatrix * vec4(aPosition,1);
vTextureCoord = aColor;
vglPositionRec = uMVMatrix * vec4(aPosition, 1);
}
我们在fragmentshander中对每一个坐标点都进行了运算,而非插值。vertexshader值负责坐标点传入。
顶点中计算光照
先上一张效果图
我日,这和上一张图是一个位置拍的吗,怎么成了这个鸟样子…..可见如果在顶点中计算光照,效果会不太理想。我们在顶点中只计算了天空盒的四个顶点,那么其他地方的光照颜色从哪里来的呢?对,就是上一篇文章中说的,是插值出来的。有了顶点色值和计算好的插值值,片元就直接用这些插值得到的色值。所以,这种方式效率比较高,但是效果可能在有些场合不太理想。
fragmentshader
precision mediump float;
uniform sampler2D sTexture;
varying vec2 vTextureCoord;
varying vec4 vglPositionRec;
varying vec3 vPointNormal;
varying vec3 uLightPosition;
varying vec3 vScatteredLight;
void main(){
gl_FragColor = min(texture2D(sTexture, vTextureCoord) * vec4(vScatteredLight, 1), vec4(1.0));
}
vertexshader
uniform mat4 uMVPMatrix;
uniform mat4 uMVMatrix;
attribute vec3 aPosition;
attribute vec2 aColor;
varying vec2 vTextureCoord;
varying vec4 vglPositionRec;//物体未经透视的位置
//传入点的法线
attribute vec3 aPointNormal;
varying vec3 vPointNormal;
//传入光源位置
attribute vec3 aLightPosition;
varying vec3 uLightPosition;
struct LightProperties{
bool isEnabled;
bool isLocal;
bool isSpot;
vec3 ambient;
vec3 color;
vec3 position;
vec3 halfVector;
vec3 coneDirection;
float spotCosCutoff;
float spotExponent;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
};
const int MaxLights = 5;//用几个光源设置几个,开多了会卡
uniform LightProperties uLights[MaxLights];
varying vec3 vScatteredLight;
void main(){
vPointNormal = normalize(aPointNormal);
gl_Position = uMVPMatrix * vec4(aPosition,1);
vTextureCoord = aColor;
vglPositionRec = uMVMatrix * vec4(aPosition, 1);
vec3 scatteredLight = vec3(0.0);
for(int light=0; light<MaxLights; light++){
if(! uLights[light].isEnabled){
continue;
}
vec3 lightDirection = uLights[light].position;
float attenuation = 1.0;
if(uLights[light].isLocal){ //点光或者聚光
lightDirection = lightDirection - vec3(vglPositionRec);
float lightDistance = length(lightDirection);
lightDirection = lightDirection / lightDistance;
attenuation = 1.0/(uLights[light].constantAttenuation +
uLights[light].linearAttenuation*lightDistance );//+
//uLights[light].quadraticAttenuation*lightDistance*lightDistance);
if(uLights[light].isSpot){
float spotCos = dot(lightDirection, -uLights[light].coneDirection);
if(spotCos < uLights[light].spotCosCutoff)
attenuation = 0.0;
else
attenuation *= pow(spotCos, uLights[light].spotExponent);
}
}
float diffuse = max(0.0, dot(vPointNormal, lightDirection));
scatteredLight += (uLights[light].ambient + uLights[light].color * diffuse) * attenuation;
}
vScatteredLight = scatteredLight;
}
我们只计算额顶点处的光照值,其他的值抛给了gl,让他自己插值。gl说好,那我就线性插值吧,于是被光照的面中间基本上看不出来什么颜色。而fragmentshader则说明也不干,你上面给我什么值,我直接用就好了。