WebGL 聚光灯
WebGL Spotlight
我必须向我的旋转立方体添加聚光灯,所以这是我的代码:
"use strict";
var canvas;
var gl;
var numVertices = 36;
var texSize = 256;
var numChecks = 8;
var program;
var texture1, texture2;
var t1, t2;
var normalsArray = [];
var dirLightPosition = vec4(1.0, 1.0, 1.0, 0.0 );
var dirLightAmbient = vec4(0.2, 0.2, 0.2, 1.0 );
var dirLightDiffuse = vec4( 1.0, 1.0, 1.0, 1.0 );
var dirLightSpecular = vec4( 1.0, 1.0, 1.0, 1.0 );
var posLightPosition = vec4(1.0, 1.0, 1.0, 1.0 );
var posLightAmbient = vec4(0.2, 0.2, 0.2, 1.0 );
var posLightDiffuse = vec4( 1.0, 1.0, 1.0, 1.0 );
var posLightSpecular = 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, 1.0, 1.0, 1.0 );
var materialShininess = 20.0;
var spotLightPosition = vec4(1.0, 2.0, 3.0, 1.0 );
var spotLightAmbient = vec4(0.2, 0.2, 0.2, 1.0 );
var spotLightDiffuse = vec4( 1.0, 1.0, 1.0, 1.0 );
var spotLightSpecular = vec4( 1.0, 1.0, 1.0, 1.0 );
var spotLightDirection = vec4(-0.5,1.0,2.0,1.0);
var lCutOff=0.867;
var c;
var flag = true;
var image1 = new Uint8Array(4*texSize*texSize);
for ( var i = 0; i < texSize; i++ ) {
for ( var j = 0; j <texSize; j++ ) {
var patchx = Math.floor(i/(texSize/numChecks));
var patchy = Math.floor(j/(texSize/numChecks));
if(patchx%2 ^ patchy%2) c = 255;
else c = 0;
//c = 255*(((i & 0x8) == 0) ^ ((j & 0x8) == 0))
image1[4*i*texSize+4*j] = c;
image1[4*i*texSize+4*j+1] = c;
image1[4*i*texSize+4*j+2] = c;
image1[4*i*texSize+4*j+3] = 255;
}
}
var image2 = new Uint8Array(4*texSize*texSize);
// Create a checkerboard pattern
for ( var i = 0; i < texSize; i++ ) {
for ( var j = 0; j <texSize; j++ ) {
image2[4*i*texSize+4*j] = 127+127*Math.sin(0.1*i*j);
image2[4*i*texSize+4*j+1] = 127+127*Math.sin(0.1*i*j);
image2[4*i*texSize+4*j+2] = 127+127*Math.sin(0.1*i*j);
image2[4*i*texSize+4*j+3] = 255;
}
}
var pointsArray = [];
var colorsArray = [];
var texCoordsArray = [];
var texCoord = [
vec2(0, 0),
vec2(0, 1),
vec2(1, 1),
vec2(1, 0)
];
var vertices = [
vec4( -0.5, -0.5, 0.5, 1.0 ),
vec4( -0.5, 0.5, 0.5, 1.0 ),
vec4( 0.5, 0.5, 0.5, 1.0 ),
vec4( 0.5, -0.5, 0.5, 1.0 ),
vec4( -0.5, -0.5, -0.5, 1.0 ),
vec4( -0.5, 0.5, -0.5, 1.0 ),
vec4( 0.5, 0.5, -0.5, 1.0 ),
vec4( 0.5, -0.5, -0.5, 1.0 )
];
var vertexColors = [
vec4( 0.0, 0.0, 0.0, 1.0 ), // black
vec4( 1.0, 0.0, 0.0, 1.0 ), // red
vec4( 1.0, 1.0, 0.0, 1.0 ), // yellow
vec4( 0.0, 1.0, 0.0, 1.0 ), // green
vec4( 0.0, 0.0, 1.0, 1.0 ), // blue
vec4( 1.0, 0.0, 1.0, 1.0 ), // magenta
vec4( 0.0, 1.0, 1.0, 1.0 ), // white
vec4( 0.0, 1.0, 1.0, 1.0 ) // cyan
];
var xAxis = 0;
var yAxis = 1;
var zAxis = 2;
var axis = xAxis;
var theta = [45.0, 45.0, 45.0];
var thetaLoc;
function configureTexture() {
texture1 = gl.createTexture();
gl.bindTexture( gl.TEXTURE_2D, texture1 );
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize, texSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, image1);
gl.generateMipmap( gl.TEXTURE_2D );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
gl.NEAREST_MIPMAP_LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
texture2 = gl.createTexture();
gl.bindTexture( gl.TEXTURE_2D, texture2 );
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize, texSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, image2);
gl.generateMipmap( gl.TEXTURE_2D );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
gl.NEAREST_MIPMAP_LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
}
function quad(a, b, c, d) {
var t1 = subtract(vertices[b], vertices[a]);
var t2 = subtract(vertices[c], vertices[b]);
var normal = cross(t1, t2);
var normal = vec3(normal);
pointsArray.push(vertices[a]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[0]);
normalsArray.push(normal);
pointsArray.push(vertices[b]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[1]);
normalsArray.push(normal);
pointsArray.push(vertices[c]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[2]);
normalsArray.push(normal);
pointsArray.push(vertices[a]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[0]);
normalsArray.push(normal);
pointsArray.push(vertices[c]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[2]);
normalsArray.push(normal);
pointsArray.push(vertices[d]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[3]);
normalsArray.push(normal);
}
function colorCube()
{
quad( 1, 0, 3, 2 );
quad( 2, 3, 7, 6 );
quad( 3, 0, 4, 7 );
quad( 6, 5, 1, 2 );
quad( 4, 5, 6, 7 );
quad( 5, 4, 0, 1 );
}
function init() {
canvas = document.getElementById( "gl-canvas" );
gl = canvas.getContext("webgl");
if ( !gl ) { alert( "WebGL isn't available" ); }
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor( 1.0, 1.0, 1.0, 1.0 );
gl.enable(gl.DEPTH_TEST);
//
// Load shaders and initialize attribute buffers
//
program = initShaders( gl, "vertex-shader", "fragment-shader" );
gl.useProgram( program );
colorCube();
var nBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, nBuffer );
gl.bufferData( gl.ARRAY_BUFFER, flatten(normalsArray), gl.STATIC_DRAW );
var vNormal = gl.getAttribLocation( program, "vNormal" );
gl.vertexAttribPointer( vNormal, 3, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vNormal );
var cBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, cBuffer );
gl.bufferData( gl.ARRAY_BUFFER, flatten(colorsArray), gl.STATIC_DRAW );
var vColor = gl.getAttribLocation( program, "vColor" );
gl.vertexAttribPointer( vColor, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vColor );
var vBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, vBuffer);
gl.bufferData( gl.ARRAY_BUFFER, flatten(pointsArray), gl.STATIC_DRAW );
var vPosition = gl.getAttribLocation( program, "vPosition" );
gl.vertexAttribPointer( vPosition, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vPosition );
var tBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, tBuffer);
gl.bufferData( gl.ARRAY_BUFFER, flatten(texCoordsArray), gl.STATIC_DRAW );
var vTexCoord = gl.getAttribLocation( program, "vTexCoord" );
gl.vertexAttribPointer( vTexCoord, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vTexCoord );
var dirAmbientProduct = mult(dirLightAmbient, materialAmbient);
var dirDiffuseProduct = mult(dirLightDiffuse, materialDiffuse);
var dirSpecularProduct = mult(dirLightSpecular, materialSpecular);
var posAmbientProduct = mult(posLightAmbient, materialAmbient);
var posDiffuseProduct = mult(posLightDiffuse, materialDiffuse);
var posSpecularProduct = mult(posLightSpecular, materialSpecular);
var spotAmbientProduct = mult(spotLightAmbient, materialAmbient);
var spotDiffuseProduct = mult(spotLightDiffuse, materialDiffuse);
var spotSpecularProduct = mult(spotLightSpecular, materialSpecular);
configureTexture();
gl.activeTexture( gl.TEXTURE0 );
gl.bindTexture( gl.TEXTURE_2D, texture1 );
gl.uniform1i(gl.getUniformLocation( program, "Tex0"), 0);
gl.activeTexture( gl.TEXTURE1 );
gl.bindTexture( gl.TEXTURE_2D, texture2 );
gl.uniform1i(gl.getUniformLocation( program, "Tex1"), 1);
thetaLoc = gl.getUniformLocation(program, "theta");
gl.uniform4fv( gl.getUniformLocation(program,
"dirAmbientProduct"),flatten(dirAmbientProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"posAmbientProduct"),flatten(posAmbientProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"spotAmbientProduct"),flatten(spotAmbientProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"dirDiffuseProduct"),flatten(dirDiffuseProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"posDiffuseProduct"),flatten(posDiffuseProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"spotDiffuseProduct"),flatten(spotDiffuseProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"dirSpecularProduct"),flatten(dirSpecularProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"posSpecularProduct"),flatten(posSpecularProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"spotSpecularProduct"),flatten(spotSpecularProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"posLightPosition"),flatten(posLightPosition) );
gl.uniform4fv( gl.getUniformLocation(program,
"dirLightPosition"),flatten(dirLightPosition) );
gl.uniform4fv( gl.getUniformLocation(program,
"spotLightPosition"),flatten(spotLightPosition) );
gl.uniform1f( gl.getUniformLocation(program,
"shininess"),materialShininess );
gl.uniform1f( gl.getUniformLocation(program,
"lCutOff"),lCutOff );
gl.uniform1f( gl.getUniformLocation(program,
"spotLightDirection"),spotLightDirection );
document.getElementById("ButtonX").onclick = function(){axis = xAxis;};
document.getElementById("ButtonY").onclick = function(){axis = yAxis;};
document.getElementById("ButtonZ").onclick = function(){axis = zAxis;};
document.getElementById("ButtonT").onclick = function(){flag = !flag;};
render();
}
var render = function() {
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
if(flag) theta[axis] += 2.0;
gl.uniform3fv(thetaLoc, theta);
gl.drawArrays( gl.TRIANGLES, 0, numVertices );
requestAnimationFrame(render);
}
// --- added to make it work ---
function initShaders(gl, vs, fs) {
return twgl.createProgramFromScripts(gl, [vs, fs]);
}
function vec2(x, y) {
return [x, y];
}
function vec3(x, y, z) {
return [x, y, z];
}
function vec4(x, y, z, w) {
return [x, y, z, w];
}
function subtract(a, b) {
return a.map((a, ndx) => { return a - b[ndx];});
}
function mult(a, b) {
return a.map((a, ndx) => { return a * b[ndx];});
}
function cross(a, b) {
return [
a[0] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
];
}
function flatten(a) {
return new Float32Array([].concat(...a));
}
init();
<button id = "ButtonX">Rotate X</button>
<button id = "ButtonY">Rotate Y</button>
<button id = "ButtonZ">Rotate Z</button>
<button id = "ButtonT">Toggle Rotation</button>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec4 vColor;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
uniform float shininess;
uniform vec4 posLightPosition;
uniform vec4 posAmbientProduct, posDiffuseProduct, posSpecularProduct;
uniform vec4 dirLightPosition;
uniform vec4 dirAmbientProduct, dirDiffuseProduct, dirSpecularProduct;
uniform vec4 spotLightPosition;
uniform vec4 spotAmbientProduct, spotDiffuseProduct,spotSpecularProduct;
uniform vec4 spotLightDirection;
uniform float lCutOff;
varying vec4 fColor;
varying vec2 fTexCoord;
uniform vec3 theta;
void main()
{
// Compute the sines and cosines of theta for each of
// the three axes in one computation.
vec3 angles = radians( theta );
vec3 c = cos( angles );
vec3 s = sin( angles );
// Remeber: thse matrices are column-major
mat4 rx = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, c.x, s.x, 0.0,
0.0, -s.x, c.x, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 ry = mat4( c.y, 0.0, -s.y, 0.0,
0.0, 1.0, 0.0, 0.0,
s.y, 0.0, c.y, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 rz = mat4( c.z, s.z, 0.0, 0.0,
-s.z, c.z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 );
vec3 N=normalize((rz*ry*rx*vec4(vNormal,0)).xyz);
// positional light
vec3 pos=-(rz*ry*rx*vPosition).xyz;
vec3 posLight=posLightPosition.xyz;
vec3 posL=normalize(posLight-pos);
vec3 posE=normalize(-pos);
vec3 posH=normalize(posL+posE);
vec4 posAmbient=posAmbientProduct;
float posKd=max(dot(posL,N),0.0);
vec4 posDiffuse=posKd*posDiffuseProduct;
float posKs=pow(max(dot(N,posH),0.0), shininess);
vec4 posSpecular=posKs*posSpecularProduct;
if(dot(posL,N)<0.0){
posSpecular=vec4(0.0,0.0,0.0,1.0);
}
// directional light
vec3 dirLight=dirLightPosition.xyz;
vec3 dirL=normalize(dirLight);
vec3 dirE=normalize(-pos);
vec3 dirH=normalize(dirL+dirE);
vec4 dirAmbient=dirAmbientProduct;
float dirKd=max(dot(dirL,N),0.0);
vec4 dirDiffuse=dirKd*dirDiffuseProduct;
float dirKs=pow(max(dot(N,dirH),0.0), shininess);
vec4 dirSpecular=dirKs*dirSpecularProduct;
if(dot(dirL,N)<0.0){
dirSpecular=vec4(0.0,0.0,0.0,1.0);
}
//spotlight
vec3 spotLight=spotLightPosition.xyz;
vec3 spotL=normalize(spotLight);
vec3 spotE=normalize(-pos);
vec3 spotH=normalize(spotL+spotE);
vec3 spotD=spotLightDirection.xyz;
vec4 spotAmbient=spotAmbientProduct;
float spotKd=max(dot(spotL,N),0.0);
vec4 spotDiffuse=spotKd*spotDiffuseProduct;
float spotKs=pow(max(dot(N,spotH),0.0), shininess);
vec4 spotSpecular=spotKs*spotSpecularProduct;
if(dot(spotL,N)<0.0){
spotSpecular=vec4(0.0,0.0,0.0,1.0);
}
float lEffect= dot(normalize(spotD), normalize((spotL+spotE)));
if(lEffect>lCutOff)
{
vec4 ambient=posAmbient+dirAmbient+spotAmbient;
vec4 diffuse=posDiffuse+dirDiffuse+spotDiffuse;
vec4 specular=posSpecular+dirSpecular+spotSpecular;
fColor = ambient+vColor*(diffuse+specular);
fColor.a = 1.0;
}
else{
vec4 ambient=posAmbient+dirAmbient;
vec4 diffuse=posDiffuse+dirDiffuse;
vec4 specular=posSpecular+dirSpecular;
fColor = ambient+vColor*(diffuse+specular);
fColor.a = 1.0;
}
fTexCoord = vTexCoord;
gl_Position = rz * ry * rx * vPosition;
gl_Position.z = -gl_Position.z;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fColor;
varying vec2 fTexCoord;
uniform sampler2D Tex0;
uniform sampler2D Tex1;
void
main()
{
gl_FragColor = fColor*(texture2D(Tex0, fTexCoord)*texture2D(Tex1, fTexCoord));
}
</script>
<script src="https://twgljs.org/dist/3.x/twgl.min.js"></script>
<canvas id="gl-canvas" width="256" height="256">
</canvas>
所以我计算了聚光灯方向和从顶点到光源的矢量之间的角度,如果这个角度大于聚光灯定义的截止角,我计算了 Phong 照明模型的三个产品。
我哪里错了?
编辑:问题的主要部分
HTML
//spotlight
vec3 spotLight=spotLightPosition.xyz;
vec3 spotL=normalize(spotLight);
vec3 spotE=normalize(-pos);
vec3 spotH=normalize(spotL+spotE);
vec3 spotD=spotLightDirection.xyz;
vec4 spotAmbient=spotAmbientProduct;
float spotKd=max(dot(spotL,N),0.0);
vec4 spotDiffuse=spotKd*spotDiffuseProduct;
float spotKs=pow(max(dot(N,spotH),0.0), shininess);
vec4 spotSpecular=spotKs*spotSpecularProduct;
if(dot(spotL,N)<0.0){
spotSpecular=vec4(0.0,0.0,0.0,1.0);
}
float lEffect= dot(normalize(spotD), normalize((spotL+spotE)));
if(lEffect>lCutOff)
{
vec4 ambient=posAmbient+dirAmbient+spotAmbient;
vec4 diffuse=posDiffuse+dirDiffuse+spotDiffuse;
vec4 specular=posSpecular+dirSpecular+spotSpecular;
fColor = ambient+vColor*(diffuse+specular);
fColor.a = 1.0;
}
else{
vec4 ambient=posAmbient+dirAmbient;
vec4 diffuse=posDiffuse+dirDiffuse;
vec4 specular=posSpecular+dirSpecular;
fColor = ambient+vColor*(diffuse+specular);
fColor.a = 1.0;
}
JAVASCRIPT
var spotLightPosition = vec4(1.0, 2.0, 3.0, 1.0 );
var spotLightAmbient = vec4(0.2, 0.2, 0.2, 1.0 );
var spotLightDiffuse = vec4( 1.0, 1.0, 1.0, 1.0 );
var spotLightSpecular = vec4( 1.0, 1.0, 1.0, 1.0 );
var spotLightDirection = vec4(-0.5,1.0,2.0,1.0);
var lCutOff=0.867;
镜面反射确实不能很好地用于逐顶点照明,特别是对于像这里的立方体这样的粗顶点模型。当按顶点完成时,它可能看起来并不像您期望的那样。具有高光泽度时,您基本上永远不会看到镜面高光,因为 "how shiny should this pixel be" 的评估仅在顶点像素上进行,并且对所有非顶点像素进行线性插值。
但是,您可以使用 shininess=0.0 来测试数学是否至少接近,这会将镜面反射着色退化为漫反射着色。
gl.uniform1f( gl.getUniformLocation(program,
"spotLightDirection"),spotLightDirection );
它不是一个浮点数而是一个 4 维向量,所以它应该是 gl.uniform4fv
。
我必须向我的旋转立方体添加聚光灯,所以这是我的代码:
"use strict";
var canvas;
var gl;
var numVertices = 36;
var texSize = 256;
var numChecks = 8;
var program;
var texture1, texture2;
var t1, t2;
var normalsArray = [];
var dirLightPosition = vec4(1.0, 1.0, 1.0, 0.0 );
var dirLightAmbient = vec4(0.2, 0.2, 0.2, 1.0 );
var dirLightDiffuse = vec4( 1.0, 1.0, 1.0, 1.0 );
var dirLightSpecular = vec4( 1.0, 1.0, 1.0, 1.0 );
var posLightPosition = vec4(1.0, 1.0, 1.0, 1.0 );
var posLightAmbient = vec4(0.2, 0.2, 0.2, 1.0 );
var posLightDiffuse = vec4( 1.0, 1.0, 1.0, 1.0 );
var posLightSpecular = 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, 1.0, 1.0, 1.0 );
var materialShininess = 20.0;
var spotLightPosition = vec4(1.0, 2.0, 3.0, 1.0 );
var spotLightAmbient = vec4(0.2, 0.2, 0.2, 1.0 );
var spotLightDiffuse = vec4( 1.0, 1.0, 1.0, 1.0 );
var spotLightSpecular = vec4( 1.0, 1.0, 1.0, 1.0 );
var spotLightDirection = vec4(-0.5,1.0,2.0,1.0);
var lCutOff=0.867;
var c;
var flag = true;
var image1 = new Uint8Array(4*texSize*texSize);
for ( var i = 0; i < texSize; i++ ) {
for ( var j = 0; j <texSize; j++ ) {
var patchx = Math.floor(i/(texSize/numChecks));
var patchy = Math.floor(j/(texSize/numChecks));
if(patchx%2 ^ patchy%2) c = 255;
else c = 0;
//c = 255*(((i & 0x8) == 0) ^ ((j & 0x8) == 0))
image1[4*i*texSize+4*j] = c;
image1[4*i*texSize+4*j+1] = c;
image1[4*i*texSize+4*j+2] = c;
image1[4*i*texSize+4*j+3] = 255;
}
}
var image2 = new Uint8Array(4*texSize*texSize);
// Create a checkerboard pattern
for ( var i = 0; i < texSize; i++ ) {
for ( var j = 0; j <texSize; j++ ) {
image2[4*i*texSize+4*j] = 127+127*Math.sin(0.1*i*j);
image2[4*i*texSize+4*j+1] = 127+127*Math.sin(0.1*i*j);
image2[4*i*texSize+4*j+2] = 127+127*Math.sin(0.1*i*j);
image2[4*i*texSize+4*j+3] = 255;
}
}
var pointsArray = [];
var colorsArray = [];
var texCoordsArray = [];
var texCoord = [
vec2(0, 0),
vec2(0, 1),
vec2(1, 1),
vec2(1, 0)
];
var vertices = [
vec4( -0.5, -0.5, 0.5, 1.0 ),
vec4( -0.5, 0.5, 0.5, 1.0 ),
vec4( 0.5, 0.5, 0.5, 1.0 ),
vec4( 0.5, -0.5, 0.5, 1.0 ),
vec4( -0.5, -0.5, -0.5, 1.0 ),
vec4( -0.5, 0.5, -0.5, 1.0 ),
vec4( 0.5, 0.5, -0.5, 1.0 ),
vec4( 0.5, -0.5, -0.5, 1.0 )
];
var vertexColors = [
vec4( 0.0, 0.0, 0.0, 1.0 ), // black
vec4( 1.0, 0.0, 0.0, 1.0 ), // red
vec4( 1.0, 1.0, 0.0, 1.0 ), // yellow
vec4( 0.0, 1.0, 0.0, 1.0 ), // green
vec4( 0.0, 0.0, 1.0, 1.0 ), // blue
vec4( 1.0, 0.0, 1.0, 1.0 ), // magenta
vec4( 0.0, 1.0, 1.0, 1.0 ), // white
vec4( 0.0, 1.0, 1.0, 1.0 ) // cyan
];
var xAxis = 0;
var yAxis = 1;
var zAxis = 2;
var axis = xAxis;
var theta = [45.0, 45.0, 45.0];
var thetaLoc;
function configureTexture() {
texture1 = gl.createTexture();
gl.bindTexture( gl.TEXTURE_2D, texture1 );
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize, texSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, image1);
gl.generateMipmap( gl.TEXTURE_2D );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
gl.NEAREST_MIPMAP_LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
texture2 = gl.createTexture();
gl.bindTexture( gl.TEXTURE_2D, texture2 );
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texSize, texSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, image2);
gl.generateMipmap( gl.TEXTURE_2D );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
gl.NEAREST_MIPMAP_LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
}
function quad(a, b, c, d) {
var t1 = subtract(vertices[b], vertices[a]);
var t2 = subtract(vertices[c], vertices[b]);
var normal = cross(t1, t2);
var normal = vec3(normal);
pointsArray.push(vertices[a]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[0]);
normalsArray.push(normal);
pointsArray.push(vertices[b]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[1]);
normalsArray.push(normal);
pointsArray.push(vertices[c]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[2]);
normalsArray.push(normal);
pointsArray.push(vertices[a]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[0]);
normalsArray.push(normal);
pointsArray.push(vertices[c]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[2]);
normalsArray.push(normal);
pointsArray.push(vertices[d]);
colorsArray.push(vertexColors[a]);
texCoordsArray.push(texCoord[3]);
normalsArray.push(normal);
}
function colorCube()
{
quad( 1, 0, 3, 2 );
quad( 2, 3, 7, 6 );
quad( 3, 0, 4, 7 );
quad( 6, 5, 1, 2 );
quad( 4, 5, 6, 7 );
quad( 5, 4, 0, 1 );
}
function init() {
canvas = document.getElementById( "gl-canvas" );
gl = canvas.getContext("webgl");
if ( !gl ) { alert( "WebGL isn't available" ); }
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor( 1.0, 1.0, 1.0, 1.0 );
gl.enable(gl.DEPTH_TEST);
//
// Load shaders and initialize attribute buffers
//
program = initShaders( gl, "vertex-shader", "fragment-shader" );
gl.useProgram( program );
colorCube();
var nBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, nBuffer );
gl.bufferData( gl.ARRAY_BUFFER, flatten(normalsArray), gl.STATIC_DRAW );
var vNormal = gl.getAttribLocation( program, "vNormal" );
gl.vertexAttribPointer( vNormal, 3, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vNormal );
var cBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, cBuffer );
gl.bufferData( gl.ARRAY_BUFFER, flatten(colorsArray), gl.STATIC_DRAW );
var vColor = gl.getAttribLocation( program, "vColor" );
gl.vertexAttribPointer( vColor, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vColor );
var vBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, vBuffer);
gl.bufferData( gl.ARRAY_BUFFER, flatten(pointsArray), gl.STATIC_DRAW );
var vPosition = gl.getAttribLocation( program, "vPosition" );
gl.vertexAttribPointer( vPosition, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vPosition );
var tBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, tBuffer);
gl.bufferData( gl.ARRAY_BUFFER, flatten(texCoordsArray), gl.STATIC_DRAW );
var vTexCoord = gl.getAttribLocation( program, "vTexCoord" );
gl.vertexAttribPointer( vTexCoord, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vTexCoord );
var dirAmbientProduct = mult(dirLightAmbient, materialAmbient);
var dirDiffuseProduct = mult(dirLightDiffuse, materialDiffuse);
var dirSpecularProduct = mult(dirLightSpecular, materialSpecular);
var posAmbientProduct = mult(posLightAmbient, materialAmbient);
var posDiffuseProduct = mult(posLightDiffuse, materialDiffuse);
var posSpecularProduct = mult(posLightSpecular, materialSpecular);
var spotAmbientProduct = mult(spotLightAmbient, materialAmbient);
var spotDiffuseProduct = mult(spotLightDiffuse, materialDiffuse);
var spotSpecularProduct = mult(spotLightSpecular, materialSpecular);
configureTexture();
gl.activeTexture( gl.TEXTURE0 );
gl.bindTexture( gl.TEXTURE_2D, texture1 );
gl.uniform1i(gl.getUniformLocation( program, "Tex0"), 0);
gl.activeTexture( gl.TEXTURE1 );
gl.bindTexture( gl.TEXTURE_2D, texture2 );
gl.uniform1i(gl.getUniformLocation( program, "Tex1"), 1);
thetaLoc = gl.getUniformLocation(program, "theta");
gl.uniform4fv( gl.getUniformLocation(program,
"dirAmbientProduct"),flatten(dirAmbientProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"posAmbientProduct"),flatten(posAmbientProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"spotAmbientProduct"),flatten(spotAmbientProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"dirDiffuseProduct"),flatten(dirDiffuseProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"posDiffuseProduct"),flatten(posDiffuseProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"spotDiffuseProduct"),flatten(spotDiffuseProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"dirSpecularProduct"),flatten(dirSpecularProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"posSpecularProduct"),flatten(posSpecularProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"spotSpecularProduct"),flatten(spotSpecularProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"posLightPosition"),flatten(posLightPosition) );
gl.uniform4fv( gl.getUniformLocation(program,
"dirLightPosition"),flatten(dirLightPosition) );
gl.uniform4fv( gl.getUniformLocation(program,
"spotLightPosition"),flatten(spotLightPosition) );
gl.uniform1f( gl.getUniformLocation(program,
"shininess"),materialShininess );
gl.uniform1f( gl.getUniformLocation(program,
"lCutOff"),lCutOff );
gl.uniform1f( gl.getUniformLocation(program,
"spotLightDirection"),spotLightDirection );
document.getElementById("ButtonX").onclick = function(){axis = xAxis;};
document.getElementById("ButtonY").onclick = function(){axis = yAxis;};
document.getElementById("ButtonZ").onclick = function(){axis = zAxis;};
document.getElementById("ButtonT").onclick = function(){flag = !flag;};
render();
}
var render = function() {
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
if(flag) theta[axis] += 2.0;
gl.uniform3fv(thetaLoc, theta);
gl.drawArrays( gl.TRIANGLES, 0, numVertices );
requestAnimationFrame(render);
}
// --- added to make it work ---
function initShaders(gl, vs, fs) {
return twgl.createProgramFromScripts(gl, [vs, fs]);
}
function vec2(x, y) {
return [x, y];
}
function vec3(x, y, z) {
return [x, y, z];
}
function vec4(x, y, z, w) {
return [x, y, z, w];
}
function subtract(a, b) {
return a.map((a, ndx) => { return a - b[ndx];});
}
function mult(a, b) {
return a.map((a, ndx) => { return a * b[ndx];});
}
function cross(a, b) {
return [
a[0] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
];
}
function flatten(a) {
return new Float32Array([].concat(...a));
}
init();
<button id = "ButtonX">Rotate X</button>
<button id = "ButtonY">Rotate Y</button>
<button id = "ButtonZ">Rotate Z</button>
<button id = "ButtonT">Toggle Rotation</button>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec4 vColor;
attribute vec2 vTexCoord;
attribute vec3 vNormal;
uniform float shininess;
uniform vec4 posLightPosition;
uniform vec4 posAmbientProduct, posDiffuseProduct, posSpecularProduct;
uniform vec4 dirLightPosition;
uniform vec4 dirAmbientProduct, dirDiffuseProduct, dirSpecularProduct;
uniform vec4 spotLightPosition;
uniform vec4 spotAmbientProduct, spotDiffuseProduct,spotSpecularProduct;
uniform vec4 spotLightDirection;
uniform float lCutOff;
varying vec4 fColor;
varying vec2 fTexCoord;
uniform vec3 theta;
void main()
{
// Compute the sines and cosines of theta for each of
// the three axes in one computation.
vec3 angles = radians( theta );
vec3 c = cos( angles );
vec3 s = sin( angles );
// Remeber: thse matrices are column-major
mat4 rx = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, c.x, s.x, 0.0,
0.0, -s.x, c.x, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 ry = mat4( c.y, 0.0, -s.y, 0.0,
0.0, 1.0, 0.0, 0.0,
s.y, 0.0, c.y, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 rz = mat4( c.z, s.z, 0.0, 0.0,
-s.z, c.z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 );
vec3 N=normalize((rz*ry*rx*vec4(vNormal,0)).xyz);
// positional light
vec3 pos=-(rz*ry*rx*vPosition).xyz;
vec3 posLight=posLightPosition.xyz;
vec3 posL=normalize(posLight-pos);
vec3 posE=normalize(-pos);
vec3 posH=normalize(posL+posE);
vec4 posAmbient=posAmbientProduct;
float posKd=max(dot(posL,N),0.0);
vec4 posDiffuse=posKd*posDiffuseProduct;
float posKs=pow(max(dot(N,posH),0.0), shininess);
vec4 posSpecular=posKs*posSpecularProduct;
if(dot(posL,N)<0.0){
posSpecular=vec4(0.0,0.0,0.0,1.0);
}
// directional light
vec3 dirLight=dirLightPosition.xyz;
vec3 dirL=normalize(dirLight);
vec3 dirE=normalize(-pos);
vec3 dirH=normalize(dirL+dirE);
vec4 dirAmbient=dirAmbientProduct;
float dirKd=max(dot(dirL,N),0.0);
vec4 dirDiffuse=dirKd*dirDiffuseProduct;
float dirKs=pow(max(dot(N,dirH),0.0), shininess);
vec4 dirSpecular=dirKs*dirSpecularProduct;
if(dot(dirL,N)<0.0){
dirSpecular=vec4(0.0,0.0,0.0,1.0);
}
//spotlight
vec3 spotLight=spotLightPosition.xyz;
vec3 spotL=normalize(spotLight);
vec3 spotE=normalize(-pos);
vec3 spotH=normalize(spotL+spotE);
vec3 spotD=spotLightDirection.xyz;
vec4 spotAmbient=spotAmbientProduct;
float spotKd=max(dot(spotL,N),0.0);
vec4 spotDiffuse=spotKd*spotDiffuseProduct;
float spotKs=pow(max(dot(N,spotH),0.0), shininess);
vec4 spotSpecular=spotKs*spotSpecularProduct;
if(dot(spotL,N)<0.0){
spotSpecular=vec4(0.0,0.0,0.0,1.0);
}
float lEffect= dot(normalize(spotD), normalize((spotL+spotE)));
if(lEffect>lCutOff)
{
vec4 ambient=posAmbient+dirAmbient+spotAmbient;
vec4 diffuse=posDiffuse+dirDiffuse+spotDiffuse;
vec4 specular=posSpecular+dirSpecular+spotSpecular;
fColor = ambient+vColor*(diffuse+specular);
fColor.a = 1.0;
}
else{
vec4 ambient=posAmbient+dirAmbient;
vec4 diffuse=posDiffuse+dirDiffuse;
vec4 specular=posSpecular+dirSpecular;
fColor = ambient+vColor*(diffuse+specular);
fColor.a = 1.0;
}
fTexCoord = vTexCoord;
gl_Position = rz * ry * rx * vPosition;
gl_Position.z = -gl_Position.z;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fColor;
varying vec2 fTexCoord;
uniform sampler2D Tex0;
uniform sampler2D Tex1;
void
main()
{
gl_FragColor = fColor*(texture2D(Tex0, fTexCoord)*texture2D(Tex1, fTexCoord));
}
</script>
<script src="https://twgljs.org/dist/3.x/twgl.min.js"></script>
<canvas id="gl-canvas" width="256" height="256">
</canvas>
所以我计算了聚光灯方向和从顶点到光源的矢量之间的角度,如果这个角度大于聚光灯定义的截止角,我计算了 Phong 照明模型的三个产品。
我哪里错了?
编辑:问题的主要部分
HTML
//spotlight
vec3 spotLight=spotLightPosition.xyz;
vec3 spotL=normalize(spotLight);
vec3 spotE=normalize(-pos);
vec3 spotH=normalize(spotL+spotE);
vec3 spotD=spotLightDirection.xyz;
vec4 spotAmbient=spotAmbientProduct;
float spotKd=max(dot(spotL,N),0.0);
vec4 spotDiffuse=spotKd*spotDiffuseProduct;
float spotKs=pow(max(dot(N,spotH),0.0), shininess);
vec4 spotSpecular=spotKs*spotSpecularProduct;
if(dot(spotL,N)<0.0){
spotSpecular=vec4(0.0,0.0,0.0,1.0);
}
float lEffect= dot(normalize(spotD), normalize((spotL+spotE)));
if(lEffect>lCutOff)
{
vec4 ambient=posAmbient+dirAmbient+spotAmbient;
vec4 diffuse=posDiffuse+dirDiffuse+spotDiffuse;
vec4 specular=posSpecular+dirSpecular+spotSpecular;
fColor = ambient+vColor*(diffuse+specular);
fColor.a = 1.0;
}
else{
vec4 ambient=posAmbient+dirAmbient;
vec4 diffuse=posDiffuse+dirDiffuse;
vec4 specular=posSpecular+dirSpecular;
fColor = ambient+vColor*(diffuse+specular);
fColor.a = 1.0;
}
JAVASCRIPT
var spotLightPosition = vec4(1.0, 2.0, 3.0, 1.0 );
var spotLightAmbient = vec4(0.2, 0.2, 0.2, 1.0 );
var spotLightDiffuse = vec4( 1.0, 1.0, 1.0, 1.0 );
var spotLightSpecular = vec4( 1.0, 1.0, 1.0, 1.0 );
var spotLightDirection = vec4(-0.5,1.0,2.0,1.0);
var lCutOff=0.867;
镜面反射确实不能很好地用于逐顶点照明,特别是对于像这里的立方体这样的粗顶点模型。当按顶点完成时,它可能看起来并不像您期望的那样。具有高光泽度时,您基本上永远不会看到镜面高光,因为 "how shiny should this pixel be" 的评估仅在顶点像素上进行,并且对所有非顶点像素进行线性插值。
但是,您可以使用 shininess=0.0 来测试数学是否至少接近,这会将镜面反射着色退化为漫反射着色。
gl.uniform1f( gl.getUniformLocation(program,
"spotLightDirection"),spotLightDirection );
它不是一个浮点数而是一个 4 维向量,所以它应该是 gl.uniform4fv
。