WebGL给场景添加光照,实现模型的层级运动、纹理、场景漫游

本文介绍了如何使用WebGL给场景添加光照,包括逐片元光照、模型的独立和层级运动以及场景漫游。通过设置顶点和法向量数组,结合着色器和js代码实现光照效果。同时,文章还提到了在添加光照过程中遇到的问题,即忘记在绘制前绑定和激活模型的缓冲区,这是一个重要的步骤。

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

效果图

效果图
先放一张最终效果图,实现了给场景添加光照、纹理,模型的独立运动、层级运动,场景漫游的效果,右上角为绘制的光源。完整源码已给出:传送门
本篇仅对给场景添加光照进行讲解。

给场景添加光照

整体思路

采用逐片元光照的方式,每个模型维护一个顶点数组和法向量数组,在向顶点数组添加顶点坐标的同时,对应向法向量数组中添加每个顶点对应的法向量。每个模型绘制前,传入该模型的材质参数,绑定其对应的顶点和法向量缓冲区并激活数组。
需要注意的是,法向量方向必须指向模型外部,若法向量方向错误将导致光照效果错误。

代码部分
着色器
<script id="vertex-shader" type="x-shader/x-vertex">

attribute  vec4 vPosition;
attribute vec4 vNormal;
varying vec3 N, L, E;
uniform vec4 lightPosition;
uniform mat4 viewMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 normalMatrix;

void main()
{
    
    
    vec3 pos = (modelViewMatrix * vPosition).xyz;

    // check for directional light

    if(lightPosition.w == 0.0) L = normalize(lightPosition.xyz);
    else L = normalize( lightPosition.xyz - pos );

    E =  -normalize(pos);
    N = normalize(vec3(normalMatrix*vNormal));

    gl_Position = projectionMatrix * viewMatrix * modelViewMatrix * vPosition;
}
</script>

<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;

uniform vec4 ambientProduct;
uniform vec4 diffuseProduct;
uniform vec4 specularProduct;
uniform float shininess;
varying vec3 N, L, E;

void main()
{
    
    
    vec4 fColor;

    vec3 H = normalize( L + E );
    vec4 ambient = ambientProduct;

    float Kd = max( dot(L, N), 0.0 );
    vec4  diffuse = Kd*diffuseProduct;

    float Ks = pow( max(dot(N, H), 0.0), shininess );
    vec4  specular = Ks * specularProduct;

    if( dot(L, N) < 0.0 ) specular = vec4(0.0, 0.0, 0.0, 1.0);

    fColor = ambient + diffuse +specular;
    fColor.a = 1.0;

    gl_FragColor = fColor;
}
</script>

顶点着色器内,首先判断根据LightPosition的w分量判断光源是点光源还是平行光,由此计算得出归一化的L向量(顶点到光源的距离),N向量为实际法向量(初始法向量 * 法向量矩阵,这里的法向量矩阵实际就等于模型的变换矩阵,是为了计算出模型在经过一系列变换之后的法向量)。片元着色器内接收js传递的ambientProduct(环境光材质)、diffuseProduct(漫反射材质)、specularProduct(镜面反射材质)、shininess(光强)。

js代码:

首先在全局定义了材质参数,依次为光源位置、光源环境光参数、光源漫反射参数、光源镜面反射参数、模型材质的环境光参数、模型材质的漫反射参数、模型材质的镜面反射参数、发光强度

var lightPosition = vec4(0.8, 0.8, 0.8, 0.0 );
var lightAmbient = vec4(0.2, 0.2, 0.2, 1.0 );
var lightDiffuse = vec4( 1.0, 1.0, 1.0, 1.0 );
var lightSpecular = vec4( 1.0, 1.0, 1.0, 1.0 );

var materialAmbient = vec4( 1.0, 0.0, 1.0, 1.0 );
var materialDiffuse = vec4( 1.0, 0.8, 0.0, 1.0);
var materialSpecular = vec4( 1.0, 0.8, 0.0, 1.0 );
var materialShininess = 100.0;

最终传入片元着色器的材质参数实际为光源和模型本身材质参数的乘积,即

var ambientProduct = mult(lightAmbient, materialAmbient);
var diffuseProduct = mult(lightDiffuse, materialDiffuse);
var specularProduct = mult(lightSpecular, materialSpecular);

添加立方体的法向量:
这里在为立方体设置顶点的同时,设置了法向量

// 顶点编号图示
//    v5----- v6
//   /|      /|
//  v1------v2|
//  | |     | |
//  | |v4---|-|v7
//  |/      |/
//  v0------v3
function drawBody(pointArray, normalArray) {
   
   
    // 身体的八个顶点(x,y,z,a)
    var bodyVertices = [
        vec4(-body[0]/2, -body[1]/3, body[2]/2, 1.0),
        vec4(-body[0]/2, body[1]*2/3, body[2]/2, 1.0),
        vec4(body[0]/2, body[1]*2/3, body[2]/2, 1.0),
        vec4(body[0]/2, -body[1]/3, body[2]/2, 1.0),
        vec4(-body[0]/2, -body[1]/3, -body[2]/2, 1.0),
        vec4(-body[0]/2, body[1]*2/3, -body[2]/2, 1.0),
        vec4(body[0]/2, body[1]*2/
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值