为什么我的球体不能完整?

时间:2016-01-05 17:16:17

标签: javascript html5 webgl

我正在尝试使用webglfundamentals中的方法在webgl中渲染一个球体,但不知怎的,在我的程序中,球体只是渲染到20%。在Chrome中我收到此错误:L ERROR :GL_INVALID_OPERATION : glDrawArrays: attempt to access out of range vertices in attribute 1

var canvas;
var gl;

 
var index = 0;

var pointsArray = [];
var normalsArray = [];
var indexArray = [];

var colorArray = [];

var near = -10;
var far = 10;
var radius = 1.5;
var theta  = 0.0;
var phi    = 0.0;
var dr = 5.0 * Math.PI/180.0;

var left = -3.0;
var right = 3.0;
var ytop =3.0;
var bottom = -3.0;

var va = vec4(0.0, 0.0, -1.0,1);
var vb = vec4(0.0, 0.942809, 0.333333, 1);
var vc = vec4(-0.816497, -0.471405, 0.333333, 1);
var vd = vec4(0.816497, -0.471405, 0.333333,1);
    
var lightPosition = vec4(0.0, 1.0, 1.0, 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.0, 0.0, 1.0 );
var materialSpecular = vec4( 1.0, 0.8, 0.0, 1.0 );
var materialShininess = 100.0;

var ctm;
var ambientColor, diffuseColor, specularColor;
var texture;
var modelViewMatrix, projectionMatrix;
var modelViewMatrixLoc, projectionMatrixLoc;
var eye;
var at = vec3(0.0, 0.0, 0.0);
var up = vec3(0.0, 1.0, 0.0);
var iBuffer;

function createSphereVertices(
      radius,
      subdivisionsAxis,
      subdivisionsHeight,
      opt_startLatitudeInRadians,
      opt_endLatitudeInRadians,
      opt_startLongitudeInRadians,
      opt_endLongitudeInRadians) {
    if (subdivisionsAxis <= 0 || subdivisionsHeight <= 0) {
      throw Error('subdivisionAxis and subdivisionHeight must be > 0');
    }

    opt_startLatitudeInRadians = opt_startLatitudeInRadians || 0;
    opt_endLatitudeInRadians = opt_endLatitudeInRadians || Math.PI;
    opt_startLongitudeInRadians = opt_startLongitudeInRadians || 0;
    opt_endLongitudeInRadians = opt_endLongitudeInRadians || (Math.PI * 2);

    var latRange = opt_endLatitudeInRadians - opt_startLatitudeInRadians;
    var longRange = opt_endLongitudeInRadians - opt_startLongitudeInRadians;

    // We are going to generate our sphere by iterating through its
    // spherical coordinates and generating 2 triangles for each quad on a
    // ring of the sphere.
    // var numVertices = (subdivisionsAxis + 1) * (subdivisionsHeight + 1);
    var positions = [];
    // var normals   = webglUtils.createAugmentedTypedArray(3, numVertices);
    // var texCoords = webglUtils.createAugmentedTypedArray(2 , numVertices);

    // Generate the individual vertices in our vertex buffer.
    for (var y = 0; y <= subdivisionsHeight; y++) {
      for (var x = 0; x <= subdivisionsAxis; x++) {
        // Generate a vertex based on its spherical coordinates
        var u = x / subdivisionsAxis;
        var v = y / subdivisionsHeight;
        var theta = longRange * u;
        var phi = latRange * v;
        var sinTheta = Math.sin(theta);
        var cosTheta = Math.cos(theta);
        var sinPhi = Math.sin(phi);
        var cosPhi = Math.cos(phi);
        var ux = cosTheta * sinPhi;
        var uy = cosPhi;
        var uz = sinTheta * sinPhi;
        positions.push(vec4(radius * ux, radius * uy, radius * uz,1.0));
        normalsArray.push(vec4(ux, uy, uz,1.0));
        // texCoords.push(1 - u, v);
      }
    }

    var numVertsAround = subdivisionsAxis + 1;
    // var indices = webglUtils.createAugmentedTypedArray(3, subdivisionsAxis * subdivisionsHeight * 2, Uint16Array);
    for (var x = 0; x < subdivisionsAxis; x++) {
      for (var y = 0; y < subdivisionsHeight; y++) {
        // Make triangle 1 of quad.
        pointsArray.push(positions[(y + 0) * numVertsAround + x]);
        pointsArray.push(positions[(y + 0) * numVertsAround + x + 1]);
        pointsArray.push(positions[(y + 1) * numVertsAround + x]);


        // Make triangle 2 of quad.
		pointsArray.push(positions[(y + 1) * numVertsAround + x]);
		pointsArray.push(positions[(y + 0) * numVertsAround + x + 1]);
		pointsArray.push(positions[(y + 1) * numVertsAround + x + 1]);
		index +=6;
      }
    }

  }



window.onload = function init() {

    canvas = document.getElementById( "gl-canvas" );
    
    gl = WebGLUtils.setupWebGL( canvas );
    if ( !gl ) { alert( "WebGL isn't available" ); }

    gl.viewport( 0, 0, canvas.width, canvas.height );
    gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
    
    gl.enable(gl.DEPTH_TEST);

    //
    //  Load shaders and initialize attribute buffers
    //
    var program = initShaders( gl, "vertex-shader", "fragment-shader" );
    gl.useProgram( program );

	createSphereVertices(1,12,12);
    ambientProduct = mult(lightAmbient, materialAmbient);
    diffuseProduct = mult(lightDiffuse, materialDiffuse);
    specularProduct = mult(lightSpecular, materialSpecular);

    

    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, 4, gl.FLOAT, false, 0, 0 );
    gl.enableVertexAttribArray( vNormal);


	
    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);

 
	
    modelViewMatrixLoc = gl.getUniformLocation( program, "modelViewMatrix" );
    projectionMatrixLoc = gl.getUniformLocation( program, "projectionMatrix" );

    

    gl.uniform4fv( gl.getUniformLocation(program, 
       "ambientProduct"),flatten(ambientProduct) );
    gl.uniform4fv( gl.getUniformLocation(program, 
       "diffuseProduct"),flatten(diffuseProduct) );
    gl.uniform4fv( gl.getUniformLocation(program, 
       "specularProduct"),flatten(specularProduct) );	
    gl.uniform4fv( gl.getUniformLocation(program, 
       "lightPosition"),flatten(lightPosition) );
    gl.uniform1f( gl.getUniformLocation(program, 
       "shininess"),materialShininess );

	
	render();
}


function render() {
    
    gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
    eye = vec3(radius*Math.sin(theta)*Math.cos(phi), 
        radius*Math.sin(theta)*Math.sin(phi), radius*Math.cos(theta));

    modelViewMatrix = lookAt(eye, at , up);
    projectionMatrix = ortho(left, right, bottom, ytop, near, far);
            
    gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(modelViewMatrix) );
    gl.uniformMatrix4fv(projectionMatrixLoc, false, flatten(projectionMatrix) );
        
    for( var i=0; i<index; i+=3) 
        gl.drawArrays( gl.TRIANGLES, i, 3);

    window.requestAnimFrame(render);
}

project1.html
<!DOCTYPE html>
<html>

<script id="vertex-shader" type="x-shader/x-vertex">

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

void main()
{
    vec3 pos = -(modelViewMatrix * vPosition).xyz;
    vec3 light = lightPosition.xyz;
    L = normalize( light - pos );
    E =  -pos;
    N = normalize( (modelViewMatrix*vNormal).xyz);
    gl_Position = projectionMatrix * 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>



<script type="text/javascript" src="./Common/webgl-utils.js"></script>
<script type="text/javascript" src="./Common/initShaders.js"></script>
<script type="text/javascript" src="./Common/MV.js"></script>
<script type="text/javascript" src="project1.js"></script>

<body>
<canvas id="gl-canvas" width="512" height="512">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</body>
</html>

这就是我只得到一个未完成的领域: sphere

1 个答案:

答案 0 :(得分:3)

我不知道这是实际的正确答案,因为我没有通过运行代码检查这一点。 但是,我只是猜测为什么会出现这个错误。

“属性变量超出范围”的主要原因

基本上,当您传递的缓冲区长度不足时会发生这种情况。 因此,您需要检查您创建的缓冲源的数量。

您的错误是":GL_INVALID_OPERATION : glDrawArrays: attempt to access out of range vertices in attribute 1"。在这种情况下,错误消息中的attribute 1表示GLSL代码中的“vNormal”属性。 因为它是您的GLSL代码中的第二个属性变量。 (如果错误代码显示为attribute 0,则此问题应由vPosition生成)

只要我看到你的代码,我认为长度与普通缓冲区和位置缓冲区不匹配。

  • 代码中的表面数:Subdivision Height * Subdivision Axis * 2
  • 代码中的位置元素计数:Subdivision Height * Subdivision Axis *6
  • 代码中的正常元素数:Subdivision Height * Subdivision Axis * 4

我想这就是问题的原因。 在这种情况下,每个顶点必须具有位置和法线,因此您的正常元素数必须为Subdivision Height * Subdivision Axis * 6

gl.vertexAttribPointer中的第二个参数

我认为你在gl.vertexAttribPointer中有关于第二个参数的错误。 这是计数,表示每个顶点需要传递多少个浮点元素。

在这种情况下,您为每个顶点推送了3个浮点元素。因此,即使在GLSL代码中使用了vec4,也需要指定3作为vPosition的参数。 通过这个论点,GPU可以为每个顶点分割这些缓冲区并将它们平行地传递到顶点着色器中。

向量中的第4个元素

这与您的问题没有太大关系。但我发现代码可能是错误的原因。

    positions.push(vec4(radius * ux, radius * uy, radius * uz,1.0));
    normalsArray.push(vec4(ux, uy, uz,1.0));

你需要了解vec4中第4个元素的含义。当此向量表示坐标时,第4个元素必须为1.但是,当此向量表示方向时,第4个元素必须为0.

因为,如果这意味着方向,方向不会受到平移变换的影响。 (我建议你学习仿射变换来理解这一点)

所以,你需要像这样重写那段代码。

    positions.push(vec4(radius * ux, radius * uy, radius * uz,1.0));
    normalsArray.push(vec4(ux, uy, uz,0));

我希望这个答案可以帮助你...