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