一、原理
image.png
1.在点光源的基础上,当ϕ>θ时,渲染光照效果
2.需要多传入一个spotDir和cos(θ)值;
3.cos(ϕ)为LightDir与SpotDir的单位向量点积;
4.ϕ>θ,即cos(ϕ)<cos(θ)
二、效果
spotLight.gif
三、实现
//顶点着色器
var VS = /*glsl*/ `
attribute vec4 a_Position;
attribute vec4 a_Color;
attribute vec2 a_Uv;
attribute vec4 a_Normal;
uniform mat4 u_MvpMatrix;
uniform mat4 u_ModelMatrix;
uniform mat4 u_NormalMatrix;
varying vec4 v_Color;
varying vec2 v_Uv;
varying vec3 v_Normal;
varying vec3 v_Position;
void main(){
gl_Position = u_MvpMatrix * a_Position;
v_Color = a_Color;
v_Uv=a_Uv;
v_Normal= normalize(vec3(u_NormalMatrix*a_Normal));
// v_Normal=a_Normal;
v_Position=(u_ModelMatrix*a_Position).rgb;
}`;
//片元着色器
var FS = /*glsl*/ `
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D u_Texture;
uniform vec3 u_AmbitionColor;
uniform vec3 u_LightPosition;
uniform vec3 u_LightColor;
uniform float u_CutOff;
uniform float u_OutCutOff;
uniform vec3 u_SpotDir;
varying vec2 v_Uv;
varying vec3 v_Normal;
varying vec3 v_Position;
varying vec4 v_Color;
void main(){
vec4 baseColor= texture2D(u_Texture,v_Uv);
// vec4 baseColor=vec4(1.0);
vec3 normal=normalize(v_Normal);
//计算光照方向
vec3 lightDirection=normalize(u_LightPosition-vec3(v_Position));
//计算光线与spotDir之间的夹角
float theta=max(dot(normalize(-lightDirection),normalize(u_SpotDir)),0.0);
//如果夹角小于切光角,则绘制,即余弦值大 theta>cos(u_CutOff)
vec3 diffuseColor;
if(theta>u_CutOff){
float dotL=max(0.0,dot(lightDirection,normal));
diffuseColor=u_LightColor*dotL;
}else if(theta>u_OutCutOff&&theta<u_CutOff){
float dotL=max(0.0,dot(lightDirection,normal));
//渐变
float value=(theta-u_OutCutOff)/(u_CutOff-u_OutCutOff);
diffuseColor=u_LightColor*dotL*smoothstep(0.0,1.0,value);
}
gl_FragColor =vec4((diffuseColor+u_AmbitionColor)*baseColor.rgb,baseColor.a);
}`;
//声明js需要的相关变量
var canvas = document.getElementById("canvas");
var gl = getWebGLContext(canvas);
async function main() {
if (!gl) {
console.log("你的浏览器不支持WebGL");
return;
}
const program = createProgram(gl, VS, FS);
if (!program) {
console.warn("创建程序失败!");
return;
}
gl.program = program;
gl.useProgram(program);
//获取内置变量的信息
getVariableLocation();
var n = createCube(gl);
if (n < 0) {
console.log("无法创建缓冲区");
return;
}
//设置底色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
//初始化纹理
await initTexture(gl, "./image/texture1.jpg", 0);
const ambitionColor = gl.getUniformLocation(gl.program, "u_AmbitionColor");
gl.uniform3fv(ambitionColor, [0.2, 0.2, 0.2]);
//设置点光源
const lightPosition = gl.getUniformLocation(gl.program, "u_LightPosition");
gl.uniform3fv(lightPosition, [-4, 4, 4]);
const lightColor = gl.getUniformLocation(gl.program, "u_LightColor");
gl.uniform3fv(lightColor, [148 / 255, 216 / 255, 146 / 255]);
var spotDir_x = 1
var spotDir_y = -1
var spotDir_z = -1
const spotDir = gl.getUniformLocation(gl.program, "u_SpotDir");
gl.uniform3fv(spotDir, [spotDir_x, spotDir_y, spotDir_z]);
const cutOff = gl.getUniformLocation(program, "u_CutOff");
const angle = 5
gl.uniform1f(cutOff, Math.cos(angle * Math.PI / 180));
const outCutOff = gl.getUniformLocation(program, "u_OutCutOff");
const outAngle = 8
gl.uniform1f(outCutOff, Math.cos(outAngle * Math.PI / 180));
//开启隐藏面清除
gl.enable(gl.DEPTH_TEST);
// gl.enable(gl.CULL_FACE)
document.addEventListener("keydown", (e) => {
if (e.key === "ArrowUp") {
spotDir_y += 0.01
} else if (e.key === "ArrowDown") {
spotDir_y -= 0.01
} else if (e.key === "ArrowLeft") {
spotDir_x -= 0.01
} else if (e.key === "ArrowRight") {
spotDir_x += 0.01
}
gl.uniform3fv(spotDir, [spotDir_x, spotDir_y, spotDir_z]);
});
//根据时间绘制
var tick = function () {
//变换角度
currentAngle = animate(currentAngle)
// gl.enable(gl.BLEND)
// gl.blendFunc(gl.SRC_ALPHA, gl.DST_ALPHA)
//绘制三角形
draw(gl, 36, currentAngle);
//重复请求
requestAnimationFrame(tick)
}
tick()
}
var g_last = Date.now()
var currentAngle = 0
function animate(currentAngle) {
//获取当前时间
var currentTime = Date.now()
timeD = currentTime - g_last
g_last = currentTime
//计算旋转的角度
currentAngle = currentAngle + (timeD * 30) / 1000
return currentAngle %= 360
}
function draw(gl, n, currentAngle) {
const program = gl.program;
//设置视角矩阵的相关信息(视点,视线,上方向)
var viewMatrix = new Matrix4();
viewMatrix.setLookAt(-7, 7, 7, 0, 0, 0, 0, 1, 0);
//设置模型矩阵的相关信息
var modelMatrix = new Matrix4();
modelMatrix.setRotate(currentAngle, 0, 1, 0);
gl.uniformMatrix4fv(program.modelMatrix, false, modelMatrix.elements);
viewMatrix.multiply(modelMatrix)
//设置透视投影矩阵
var projMatrix = new Matrix4();
projMatrix.setPerspective(30, canvas.width / canvas.height, 1, 100);
projMatrix.multiply(viewMatrix)
//将试图矩阵传给u_ViewMatrix变量
// gl.uniformMatrix4fv(program.mvpMatrix, false, projMatrix.elements);
gl.uniformMatrix4fv(program.mvpMatrix, false, projMatrix.elements);
var normalMatrix = new Matrix4()
normalMatrix.setInverseOf(modelMatrix)
normalMatrix.transpose()
gl.uniformMatrix4fv(program.normalMatrix, false, normalMatrix.elements);
//清空颜色和深度缓冲区
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//绘制图形
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}













网友评论