17.光照(点光源)

1.点光源产生的漫反射光

    <漫反射光颜色>=<入射光颜色>x<表面基底色>x cosB

    <漫反射光颜色>=<入射光颜色>x<表面基底色>x(<光线方向>x<法线方向>)

    由于点光源产生的光线在物体表面任意一点的入射角度都不相同,需要先计算入射方向矢量(由点光源指向顶点),根据矢量做减法在几何中的意义:

        <光线方向>=<光源位置矢量>-<顶点矢量>

'vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n'

注意:光源位置在设定时使用的是世界坐标系,而顶点位置使用的是canvas坐标系,需要先将顶点位置坐标转变成世界坐标系

'vec4 vertexPosition = u_ModelMatrix * a_Position;\n'

WebGL坐标系之间的转换:

var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'attribute vec4 a_Normal;\n' +
'uniform mat4 u_NormalMatrix;\n' +
'uniform mat4 u_ModelMatrix;\n' +
'uniform vec3 u_LightPosition;\n' +
'uniform vec3 u_LightColor;\n' +
'uniform mat4 u_MvpMatrix;\n' +
'uniform vec3 u_AmbientLight;\n' +
'varying vec4 v_Color;\n' +
'void main(){\n' +
'gl_Position = u_MvpMatrix * a_Position;\n' +
//对法向量进行归一化
'vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +
//计算顶点世界坐标
'vec4 vertexPosition = u_ModelMatrix * a_Position;\n' +
//计算光线方向并归一化
'vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n' +
//计算光线方向和法向量乘积,已经完成归一化,u_LightDirection在js代码中已经完成归一化
'float nDotL = max(dot(lightDirection,normal),0.0);\n' +
//计算漫反射光的颜色
'vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;\n'+
//计算环境光产生的反射光颜色
'vec3 ambient = u_AmbientLight * a_Color.rgb;\n'+
//a_Color.a是GLSL ES中对矢量的访问符,r,g,b,a
'v_Color = vec4(diffuse + ambient,a_Color.a);\n' +
'}\n';

var FSHADER_SOURCE =
'precision mediump float;\n'+
'varying vec4 v_Color;\n' +
'void main(){\n' +
'gl_FragColor = v_Color;\n' +
'}\n';

function main(){
var canvas =document.getElementById('webgl');
var gl = canvas.getContext('webgl');
initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE);
var n = initVertexBuffers(gl);
//开启深度检测
gl.enable(gl.DEPTH_TEST);
gl.clearColor(1.0,0.0,0.0,1.0);

//获取顶点着色器中变量
var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
var u_ModelMatrix = gl.getUniformLocation(gl.program,'u_ModelMatrix');
var u_LightPosition = gl.getUniformLocation(gl.program,'u_LightPosition');
var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
var u_NormalMatrix = gl.getUniformLocation(gl.program,'u_NormalMatrix');
//设置光线颜色
gl.uniform3f(u_LightColor,1.0,1.0,1.0);
//设置光源位置
gl.uniform3f(u_LightPosition,0.0,3.0,4.0);
//设置模型矩阵
var modelMatrix = new Matrix4();
modelMatrix.setRotate(90,0,1,0);
gl.uniformMatrix4fv(u_ModelMatrix,false,modelMatrix.elements);

//对环境光进行赋值
var u_AmbientLight = gl.getUniformLocation(gl.program,'u_AmbientLight');
gl.uniform3f(u_AmbientLight,0.2,0.2,0.2);

//对法向量矩阵赋值
var normalMatrix = new Matrix4();
normalMatrix.setInverseOf(modelMatrix);
normalMatrix.transpose();
gl.uniformMatrix4fv(u_NormalMatrix,false,normalMatrix.elements);

//计算模型视图投影矩阵
var mvpMatrix = new Matrix4();
mvpMatrix.setPerspective(30,canvas.width/canvas.height,1,100);
mvpMatrix.lookAt(3,3,7,0,0,0,0,1,0);
mvpMatrix.multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix,false,mvpMatrix.elements);



gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);
}

function initVertexBuffers(gl){
var verteices = new Float32Array([
1.0,1.0,1.0, -1.0,1.0,1.0, -1.0,-1.0,1.0, 1.0,-1.0,1.0, //前面顶点
1.0,-1.0,-1.0,1.0,1.0,-1.0, 1.0,1.0,1.0, 1.0,-1.0,1.0, //右面顶点
1.0,-1.0,-1.0, 1.0,1.0,-1.0,-1.0,1.0,-1.0, -1.0,-1.0,-1.0,//后面顶点
-1.0,-1.0,-1.0,-1.0,1.0,-1.0,-1.0,1.0,1.0, -1.0,-1.0,1.0, //左面顶点
1.0,1.0,1.0, 1.0,1.0,-1.0, -1.0,1.0,-1.0, -1.0,1.0,1.0, //顶面顶点
1.0,-1.0,1.0, 1.0,-1.0,-1.0, -1.0,-1.0,-1.0, -1.0,-1.0,1.0
]);
var normals = new Float32Array([
0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0,//前面顶点法向量
1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,//右面顶点法向量
0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0,//后面顶点法向量
-1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0,//左面顶点法向量
0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0,//顶面顶点法向量
0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0//底面顶点法向量
]);
var colors = new Float32Array([
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //前面的顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //右面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //后面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //左面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //顶面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //底面顶点颜色
]);
var indices = new Uint8Array([
0,1,2,0,2,3, //前面的顶点索引
4,5,6,4,6,7, //右面顶点索引
8,9,10,8,10,11, //后面顶点索引
12,13,14,12,14,15, //左面顶点索引
16,17,18,16,18,19, //顶面顶点索引
20,21,22,20,22,23 //底面顶点索引
]);
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);

initArrayBuffer(gl,verteices,3,gl.FLOAT,'a_Position');
initArrayBuffer(gl,colors,3,gl.FLOAT,'a_Color');
initArrayBuffer(gl,normals,3,gl.FLOAT,'a_Normal');
return indices.length;
}
function initArrayBuffer(gl,data,num,type,attribute){
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
var a_attribute = gl.getAttribLocation(gl.program,attribute);
gl.vertexAttribPointer(a_attribute,num,type,false,0,0);
gl.enableVertexAttribArray(a_attribute);
return true;
}

  2.逐片元计算反射光的颜色,当场景中有多个点光源,就需要通过逐片元计算每个点光源的反射光颜色,进行相加

    思路:(1)使用varying将变换后的顶点的世界坐标传入片元着色器,则片元着色器中接受到了每个片元坐标,即顶点矢量

'varying vec3 v_Position;\n' +

'v_Position = vec3(u_ModelMatrix * a_Position);\n' +

              (2)在顶点着色器中进行计算光线方向矢量,并进行归一化,光线方向矢量 = 光源位置矢量 - 顶点矢量。

'vec3 lightDirection = normalize(u_LightPosition - v_Position);\n' +

              (3)使用varying变量将经过(平移、旋转、缩放)后的法向量归一化后传入片元着色器,

'varying vec3 v_Normal;\n' +

v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +

              (4)使用varying将内插后的原片元颜色传入顶点着色器,

'varying vec4 v_Color;\n' +

'v_Color = a_Color;\n'+

              (5)在片元着色器中计算<光源颜色>x<片元颜色>x(<光线向量>.<法向量>)

    //计算光线方向和法向量的点积

    'float nDotL = max(dot(lightDirection,normal),0.0);\n' +

    //计算漫反射光颜色

    'vec3 diffuse = u_LightColor * vec3(v_Color) * nDotL;' +

             (6)在片元着色器中计算环境光的反射光

'uniform vec3 u_AmbientLight;\n' +

'vec3 ambient = u_AmbientLight * v_Color.rgb;\n'+

             (7)在片元着色器中计算反射光颜色

'gl_FragColor = vec4(diffuse + ambient,v_Color.a);\n' +

    总的来说:顶点着色器和片元着色器分工如下:

        顶点着色器:

            (1)计算模型、视图、投影变换后的顶点坐标,赋值给gl_Position

            (2)计算顶点的世界坐标,使用varying变量,将内插后片元的坐标传递给片元着色器

            (3)计算经过模型变换后的法向量并进行归一化,将内插后的每一片元的法向量传递给顶点着色器

            (4)将顶点颜色通过varying修饰符,将内插后每一片元的原本颜色传递给片元着色器

        片元着色器:

            (1)将片元法向量进行归一化

            (2)计算光线方向向量并进行归一化

            (3)计算点光源的漫反射光颜色

            (4)计算环境光的反射颜色

            (5)计算总反射光的颜色

4.逐片元计算反射光颜色程序实现:

var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Normal;\n' +
'attribute vec4 a_Color;\n' +
'uniform mat4 u_NormalMatrix;\n' +
'uniform mat4 u_ModelMatrix;\n' +
'uniform mat4 u_MvpMatrix;\n' +
'varying vec4 v_Color;\n' +
'varying vec3 v_Normal;\n' +
'varying vec3 v_Position;\n' +
'void main(){\n' +
'gl_Position = u_MvpMatrix * a_Position;\n' +
//计算顶点世界坐标
'v_Position = vec3(u_ModelMatrix * a_Position);\n' +
//对法向量进行归一化
'v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +
'v_Color = a_Color;\n'+
'}\n';

var FSHADER_SOURCE =
'precision mediump float;\n' +
'uniform vec3 u_LightColor;\n' +
'uniform vec3 u_LightPosition;\n' +
'uniform vec3 u_AmbientLight;\n' +
'varying vec3 v_Normal;\n' +
'varying vec3 v_Position;\n'+
'varying vec4 v_Color;\n' +
'void main(){\n' +
//计算法向量并归一化
'vec3 normal = normalize(v_Normal);\n' +
//计算光线方向并归一化
'vec3 lightDirection = normalize(u_LightPosition - v_Position);\n' +
//计算光线方向和法向量的点积
'float nDotL = max(dot(lightDirection,normal),0.0);\n' +
//计算漫反射光颜色
'vec3 diffuse = u_LightColor * vec3(v_Color) * nDotL;' +
'vec3 ambient = u_AmbientLight * v_Color.rgb;\n'+
'gl_FragColor = vec4(diffuse + ambient,v_Color.a);\n' +
'}\n';

function main(){
var canvas =document.getElementById('webgl');
var gl = canvas.getContext('webgl');
initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE);
var n = initVertexBuffers(gl);
//开启深度检测
gl.enable(gl.DEPTH_TEST);
gl.clearColor(1.0,0.0,0.0,1.0);

//获取顶点着色器中变量
var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
var u_ModelMatrix = gl.getUniformLocation(gl.program,'u_ModelMatrix');
var u_LightPosition = gl.getUniformLocation(gl.program,'u_LightPosition');
var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
var u_NormalMatrix = gl.getUniformLocation(gl.program,'u_NormalMatrix');
//设置光线颜色
gl.uniform3f(u_LightColor,1.0,1.0,1.0);
//设置光源位置
gl.uniform3f(u_LightPosition,0.0,3.0,4.0);
//设置模型矩阵
var modelMatrix = new Matrix4();
modelMatrix.setRotate(90,0,1,0);
gl.uniformMatrix4fv(u_ModelMatrix,false,modelMatrix.elements);

//对环境光进行赋值
var u_AmbientLight = gl.getUniformLocation(gl.program,'u_AmbientLight');
gl.uniform3f(u_AmbientLight,0.2,0.2,0.2);

//对法向量矩阵赋值
var normalMatrix = new Matrix4();
normalMatrix.setInverseOf(modelMatrix);
normalMatrix.transpose();
gl.uniformMatrix4fv(u_NormalMatrix,false,normalMatrix.elements);

//计算模型视图投影矩阵
var mvpMatrix = new Matrix4();
mvpMatrix.setPerspective(30,canvas.width/canvas.height,1,100);
mvpMatrix.lookAt(3,3,7,0,0,0,0,1,0);
mvpMatrix.multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix,false,mvpMatrix.elements);

gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);
}

function initVertexBuffers(gl){
var verteices = new Float32Array([
1.0,1.0,1.0, -1.0,1.0,1.0, -1.0,-1.0,1.0, 1.0,-1.0,1.0, //前面顶点
1.0,-1.0,-1.0,1.0,1.0,-1.0, 1.0,1.0,1.0, 1.0,-1.0,1.0, //右面顶点
1.0,-1.0,-1.0, 1.0,1.0,-1.0,-1.0,1.0,-1.0, -1.0,-1.0,-1.0,//后面顶点
-1.0,-1.0,-1.0,-1.0,1.0,-1.0,-1.0,1.0,1.0, -1.0,-1.0,1.0, //左面顶点
1.0,1.0,1.0, 1.0,1.0,-1.0, -1.0,1.0,-1.0, -1.0,1.0,1.0, //顶面顶点
1.0,-1.0,1.0, 1.0,-1.0,-1.0, -1.0,-1.0,-1.0, -1.0,-1.0,1.0
]);
var normals = new Float32Array([
0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0,//前面顶点法向量
1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0,//右面顶点法向量
0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0,//后面顶点法向量
-1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0,//左面顶点法向量
0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0,//顶面顶点法向量
0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0//底面顶点法向量
]);
var colors = new Float32Array([
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //前面的顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //右面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //后面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //左面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //顶面顶点颜色
0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, 0.4,1.0,0.4, //底面顶点颜色
]);
var indices = new Uint8Array([
0,1,2,0,2,3, //前面的顶点索引
4,5,6,4,6,7, //右面顶点索引
8,9,10,8,10,11, //后面顶点索引
12,13,14,12,14,15, //左面顶点索引
16,17,18,16,18,19, //顶面顶点索引
20,21,22,20,22,23 //底面顶点索引
]);
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);

initArrayBuffer(gl,verteices,3,gl.FLOAT,'a_Position');
initArrayBuffer(gl,colors,3,gl.FLOAT,'a_Color');
initArrayBuffer(gl,normals,3,gl.FLOAT,'a_Normal');
return indices.length;
}
function initArrayBuffer(gl,data,num,type,attribute){
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
var a_attribute = gl.getAttribLocation(gl.program,attribute);
gl.vertexAttribPointer(a_attribute,num,type,false,0,0);
gl.enableVertexAttribArray(a_attribute);
return true;
}

 

### Three.js 中点光源的使用方法 在 Three.js 中,通过 `THREE.PointLight` 对象可以创建一个点光源。此对象允许设置多个属性以控制光照的效果。 #### 创建点光源 要创建并配置一个简单的点光源,需指定其颜色、强度和其他参数: ```javascript // 定义点光源的颜色为白色 (0xffffff),强度为 1,影响范围最大距离为 100 单位长度 var pointLight = new THREE.PointLight(0xffffff, 1, 100); pointLight.position.set(50, 50, 50); // 设置光源的位置坐标 scene.add(pointLight); // 将光源添加到场景中 ``` 上述代码片段展示了如何初始化一个新的点光源实例,并将其放置于三维空间内的特定位置[^1]。 #### 配置点光源属性 - **颜色 (`color`)**:定义发出光线的颜色,默认值为白色(即 RGB 的十六进制表示法 `#ffffff`)。可以通过修改该属性改变光源色彩。 - **可见性 (`visible`)**:布尔型变量用于决定光源是否被渲染显示出来。默认情况下是 true 表示可见;如果设为 false,则不会参与计任何照明效果[^2]。 - **强度 (`intensity`)**:决定了光强的程度,默认值通常设定为 1. 较高的数值意味着更亮更强力的照射。 - **衰减距离 (`distance`)**:指定了光能传播的最大有效半径,在这个范围内亮度会逐渐减弱直到消失。当 distance 设定为 0 或者不提供时,理论上认为光具有无限远的作用范围。 - **位置 (`position`)**:用来调整光源所在的空间坐标系中的具体地点。这直接影响着受照物体表面接收到的不同角度和方向上的光照情况。 #### 实际应用案例展示 下面给出一段完整的 JavaScript 代码作为例子,演示了怎样在一个 web 页面上利用 Three.js 库实现带有简单几何体模型加上单一点光源照亮整个场景的功能: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <title>Three.js Point Light Example</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <style type="text/css">body { margin: 0; }</style> </head> <body> <script> const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.z = 5; const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 添加立方体网格材质的对象至场景内 const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshStandardMaterial({ color: '#ffcc00', }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); // 构建并加入点光源 const lightColor = 0xffeeff; const intensityValue = 2; const maxDistance = 30; let pointLightInstance = new THREE.PointLight(lightColor, intensityValue, maxDistance); pointLightInstance.position.set(-10, 10, 10); scene.add(pointLightInstance); function animate() { requestAnimationFrame(animate); // 让方块旋转起来 cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render(scene, camera); } animate(); window.addEventListener('resize', () => { const width = window.innerWidth; const height = window.innerHeight; renderer.setSize(width, height); camera.aspect = width / height; camera.updateProjectionMatrix(); }); </script> </body> </html> ``` 这段 HTML 和 JS 综合脚本不仅包含了前面提到的基础知识点,还实现了动态更新视窗大小后的响应式布局处理逻辑[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值