您的位置:首页 > 运维架构

基础光照模型公式与源码【GLSL】

2016-07-20 19:20 471 查看

ADS 光照模型:

Ambient lignt (light that is always present at all points in a scene)

Diffuse light (light that comes directly from a light source)

Specular light (light that is reflected in a "shiny" way from a light source by an object)

The setup for ADS (Ambient Diffuse Specular) lighting.



E:表示物体表面上一点到相机的单位向量

L:表示物体表面上一点到光源的单位向量

N:表示物体表面上一点的单位法线向量

R:表示物体表面上一点相对光源的反射单位向量

Ambient light 由 ambient light 自身 LA 和 材质被照的ambient light color MA 的乘积计算得到:

A = LA * MA

Diffuse light 由 diffuse light 自身 LD 和材质被照的 diffuse light color MD ,以及光线和法线夹角的cosin 的乘积计算得到:

D = LD * MD * (L dot N)

Specular light 由 specular light 自身 LS 和材质被照的 specular light color MS ,以及反射光线和视线夹角的cosin 的SH次幂的乘积计算得到:

S = LS * MS * (R dot E)^SH

R = 2*(N dot L)*N - L (注意图中L的方向与GLSL中的reflect相反)

最后物体表面上某点的光照为 A + D + S

ADS光照模型源码:

vec3 ADSLightModel(in vec3 myNormal, in vec3 myVertexPosition)
{
const vec3 myLightPosition = vec3(5., 5., 10.);

const vec3 myLightAmbient = vec3(.2, .2, .2);
const vec3 myLightDiffuse = vec3(1., 1., 1.);
const vec3 myLightSpecular = vec3(1., 1., 1.);

const vec3 myMaterialAmbient = vec3(1., .5, .0);
const vec3 myMaterialDiffuse = vec3(1., .5, .0);
const vec3 myMaterialSpecular = vec3(.6, .6, .6);;

const float myMaterialShininess = 80.;
// normal, light, view, and light reflection vectors
vec3 norm = normalize(myNormal);
vec3 lightv = normalize(myLightPosition - myVerexPosition);
vec3 viewv = normalize(vec3(0., 0., 0.) - myVertexPosition);<span style="white-space:pre">	</span>// EyePosition = vec3(0, 0, 0)
vec3 refl = reflect(vec3(0., 0., 0.) - lightv, norm);
// ambient light computation
vec3 ambient = myMaterialAmbient*myLightAmbient;
// diffuse light computation
vec3 diffuse = max(0., dot(lightv, norm)) * myMaterialDiffuse * myLightDiffuse;
// Optionally you can add a diffuse attenuation term at this point
// specular light computation
vec3 specular = vec3(0., 0., 0.);
if(dot(lightv, viewv)>0.)
{
specular = pow(max(0., dot(viewv, refl)),
myMaterialShininess) * myMaterialSpecular * myLightSpecular;
}

return clamp(ambient + diffuse + specular, 0., 1.);
}


如果考虑物体随着距离光源的远近进行衰减的情况

那么AT = 1 / (AC + AL*D + AQ*D^2)

(其中AC表示常数衰减系数,AL表示线性衰减系数, AQ表示二次衰减系数,D表示eye space中物体某点到光源的距离)

然后,将AT乘上之前计算出的 diffuse 和 specular 即可。

如果光源是Spot light的类型

就既要考虑距离光源的距离,还要考虑视线和光源所成的角度的cosin,即dot(L, E),接着通过已知spot light的cos(angle),以及衰减范围,利用smoothstep进行插值即可。

Phong 光照模型

其与ADL光照模型的区别是,它是一个逐片元的计算,通过对图元的顶点法线进行插值,并将ADS模型分别运用于每一个像素上。

而且,它不再计算的单位反射向量,而是通过“half angle”——the vector H halfway between the light L and the eye E vectors(GLSL 通过 normalize(L + E) 计算),以及H和N的cosin来计算specular light。

S = LS * MS * (N dot H)^SH

两种不同计算方式的效果差别:左图为“half angle”右图为"full angle"



FULL ANGLE:

Vertex Shader:

uniform vec3 fvLightPosition;
uniform vec3 fvEyePosition;

varying vec2 Texcoord;
varying vec3 ViewDirection;
varying vec3 LightDirection;
varying vec3 Normal;

void main( void )
{
gl_Position = ftransform();
Texcoord    = gl_MultiTexCoord0.xy;

vec4 fvObjectPosition = gl_ModelViewMatrix * gl_Vertex;

ViewDirection  = fvEyePosition - fvObjectPosition.xyz;
LightDirection = fvLightPosition - fvObjectPosition.xyz;
Normal         = gl_NormalMatrix * gl_Normal;

}


注:gl_NormalMatrix 为 gl_ModelViewMatrix 的逆转置矩阵,用于将gl_Normal从模型空间转换到视图空间

Fragment Shader:

uniform vec4 fvAmbient;
uniform vec4 fvSpecular;
uniform vec4 fvDiffuse;
uniform float fSpecularPower;

uniform sampler2D baseMap;

varying vec2 Texcoord;
varying vec3 ViewDirection;
varying vec3 LightDirection;
varying vec3 Normal;

void main( void )
{
vec3  fvLightDirection = normalize( LightDirection );
vec3  fvNormal         = normalize( Normal );
float fNDotL           = dot( fvNormal, fvLightDirection );

vec3  fvReflection     = normalize( ( ( 2.0 * fvNormal ) * fNDotL ) - fvLightDirection );
vec3  fvViewDirection  = normalize( ViewDirection );
float fRDotV           = max( 0.0, dot( fvReflection, fvViewDirection ) );

vec4  fvBaseColor      = texture2D( baseMap, Texcoord );

vec4  fvTotalAmbient   = fvAmbient * fvBaseColor;
vec4  fvTotalDiffuse   = fvDiffuse * fNDotL * fvBaseColor;
vec4  fvTotalSpecular  = fvSpecular * ( pow( fRDotV, fSpecularPower ) );

gl_FragColor = ( fvTotalAmbient + fvTotalDiffuse + fvTotalSpecular );

HALF ANGLE:

Fragment Shader:

uniform vec4 fvAmbient;
uniform vec4 fvSpecular;
uniform vec4 fvDiffuse;
uniform float fSpecularPower;

uniform sampler2D baseMap;

varying vec2 Texcoord;
varying vec3 ViewDirection;
varying vec3 LightDirection;
varying vec3 Normal;

void main( void )
{
vec3  fvLightDirection = normalize( LightDirection );
vec3  fvNormal         = normalize( Normal );
float fNDotL           = dot( fvNormal, fvLightDirection );

vec3  fvReflection     = normalize( ( ( 2.0 * fvNormal ) * fNDotL ) - fvLightDirection );
vec3  fvViewDirection  = normalize( ViewDirection );

// -----------------------
vec3 fvHalf = normalize(fvLightDirection + fvViewDirection);
// -----------------------

vec4  fvBaseColor      = texture2D( baseMap, Texcoord );

vec4  fvTotalAmbient   = fvAmbient * fvBaseColor;
vec4  fvTotalDiffuse   = fvDiffuse * fNDotL * fvBaseColor;
// -----------------------
float fNDotH = max(0.0, dot(fvNormal, fvHalf));
vec4 fvTotalSpecular = fvSpecular * (pow(fNDotH, fSpecularPower));
// -----------------------
gl_FragColor = ( fvTotalAmbient + fvTotalDiffuse + fvTotalSpecular );

}


Anisotropic 光照模型

该模型也是逐片元计算的,但是它在物体表面的specular 在所有方向不是均匀变化的,所以,它的specular 公式改为了以下形式:

dl = T dot L

de = T dot E

S = LS * MS * (dl * de + (1-dl*dl)^1/2 * (1-de*de)^1/2)^SH

其中,T表示tangent vector,即direction of the brushing or hair

Anisotroipc光照模型的效果



Vertex Shader:

uniform mat4 view_proj_matrix;

attribute vec3 rm_Binormal;
attribute vec3 rm_Tangent;

varying vec3 vNormal;
varying vec3 vTangent;
varying vec3 vBinormal;
varying vec3 vViewVec;
varying vec3 vPos;

void main(void)
{
gl_Position = ftransform();
// 从模型空间(不是切向空间)转换到视图空间
vTangent  = gl_NormalMatrix * rm_Tangent;
vNormal   = gl_NormalMatrix * gl_Normal;
vBinormal = gl_NormalMatrix * rm_Binormal;

vViewVec.xyz    = vec3(  gl_ModelViewMatrix * gl_Vertex );
// OpenGL has a different handedness, so we need to flip the z.
vViewVec.z  = -vViewVec.z;

//vPos = gl_Vertex.xyz * noiseRate;
vPos = gl_Vertex.xyz;

}


Fragment Shader:

uniform vec4 lightDir;
uniform vec4 gloss;
uniform vec4 color;
uniform float noiseScale;

varying vec3 vNormal;
varying vec3 vTangent;
varying vec3 vBinormal;
varying vec3 vViewVec;
varying vec3 vPos;

void main(void)
{
vec3 viewVec = normalize(vViewVec);
vec3 oglLightDir = vec3(lightDir.x, lightDir.y, -lightDir.z);

float diffuse = clamp( dot( oglLightDir.xyz, vNormal ), 0.0, 1.0 );

float angle = 0.2 * 3.14159;
float cosA, sinA;
sinA = sin(angle);
cosA = cos(angle);

// 绕着法线方向旋转A弧度后的新切线方向,
// 可以设想为vBinormal为x轴,vTangent为y轴,vNormal为z轴
vec3 tang =  sinA * vTangent + cosA * vBinormal;
float de = -dot(viewVec, tang);
float sn = sqrt(1.0 - de * de);
float dl =  dot(oglLightDir.xyz, tang);
float sl = sqrt(1.0 - dl * dl);
float specular = pow( clamp( (de * dl + sn * sl), 0.0, 1.0 ), 32.0);

gl_FragColor = diffuse * color + gloss * specular;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  OpenGL