Невозможно отобразить вращающуюся треугольную пирамиду

#javascript #html #graphics #webgl

#javascript #HTML #графика #webgl

Вопрос:

Цель: я пытаюсь отобразить треугольную пирамиду с разными
перспективными видами при изменении значений zNear, zFar, radius, theta, phi, fov и aspect с помощью соответствующих ползунков. а кнопки поворота x, y, z предназначены для поворота пирамиды на разные оси.

Проблема: Пока я придумал приведенный ниже код, все выглядит на месте, но я не вижу пирамиды, она выдает ошибку, когда я пытаюсь отобразить HTML-файл.

 var canvas;
var gl;

var NumVertices  = 12;

var pointsArray = [];
var colorsArray = [];

var xAxis = 0;
var yAxis = 1;
var zAxis = 2;

var axis = 0;
var theta = [ 0, 0, 0 ];

var near = 0.3;
var far = 3.0;
var radius = 4.0;
var theta  = 0.0;
var phi    = 0.0;
var dr = 5.0 * Math.PI/180.0;

//var thetaLoc;

var  fovy = 45.0;  // Field-of-view in Y direction angle (in degrees)
var  aspect = 1.0;       // Viewport aspect ratio

var modelViewMatrix, projectionMatrix;
var modelViewMatrixLoc, projectionMatrixLoc;
var eye;
const at = vec3(0.0, 0.0, 0.0);
const up = vec3(0.0, 1.0, 0.0);

var vertices = [
    vec3(0.5, -0.2722, 0.2886),
    vec3(0.0, -0.2722, -0.5773),
    vec3(-0.5, -0.2722, 0.2886),
    vec3(0.5, -0.5443, 0.0)
]

var vertexColors = [
    [ 0.0, 0.0, 0.0, 1.0 ],  // black
    [ 1.0, 0.0, 0.0, 1.0 ],  // red
    [ 1.0, 1.0, 0.0, 1.0 ],  // yellow
    [ 0.0, 1.0, 0.0, 1.0 ]  // green

];

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 );
    
    aspect =  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
    //
    var program = initShaders( gl, "vertex-shader", "fragment-shader" );
    gl.useProgram( program );
    
    colorPyramid();

    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 );
    
    modelViewMatrixLoc = gl.getUniformLocation( program, "modelViewMatrix" );
    projectionMatrixLoc = gl.getUniformLocation( program, "projectionMatrix" );

// sliders for viewing parameters

    document.getElementById("zFarSlider").onchange = function() {
        far = event.srcElement.value;
    };
    document.getElementById("zNearSlider").onchange = function() {
        near = event.srcElement.value;
    };
    document.getElementById("radiusSlider").onchange = function() {
       radius = event.srcElement.value;
    };
    document.getElementById("thetaSlider").onchange = function() {
        theta = event.srcElement.value* Math.PI/180.0;
    };
    document.getElementById("phiSlider").onchange = function() {
        phi = event.srcElement.value* Math.PI/180.0;
    };
    document.getElementById("aspectSlider").onchange = function() {
        aspect = event.srcElement.value;
    };
    document.getElementById("fovSlider").onchange = function() {
        fovy = event.srcElement.value;
    };

    render();
}

function colorPyramid()
{
    quad( 0, 1, 2 ); // bottom
    quad( 1, 0, 3 ); // side0
    quad( 1, 2, 3 ); // side1
    quad( 2, 0, 3 ); // side3

}

function quad(a, b, c) 
{

    var indices = [ a, b, c];
    var ndx = pointsArray.length % vertexColors.length;
    for ( var i = 0; i < indices.length;   i ) {
        pointsArray.push( vertices[indices[i]] );

        colorsArray.push(vertexColors[ndx]);
        
    }
}

function render()
{
    // gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    theta[axis]  = 2.0;
    // gl.uniform3fv(thetaLoc, theta);

    // gl.drawArrays( gl.TRIANGLES, 0, NumVertices );

    // requestAnimFrame( 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 = perspective(fovy, aspect, near, far);

    gl.uniformMatrix4fv( modelViewMatrixLoc, false, flatten(modelViewMatrix) );
    gl.uniformMatrix4fv( projectionMatrixLoc, false, flatten(projectionMatrix) );
            
    gl.drawArrays( gl.TRIANGLES, 0, NumVertices );
    requestAnimFrame(render);
}  
 <div>
zNear .01<input id="zNearSlider" type="range"
 min=".01" max="3" step="0.1" value="0.3" />
 3
</div>
<div>
zFar 3<input id="zFarSlider" type="range"
 min="3" max="10" step="3.0" value="3" />
 10
</div>
<div>
radius 0.05<input id="radiusSlider" type="range"
 min="0.05" max="10" step="0.1" value="4" />
 10
</div>
<div>
theta -90<input id="thetaSlider" type="range"
 min="-90" max="90" step="5" value="0" />
 90
</div>
<div>
phi -90<input id="phiSlider" type="range"
 min="-90" max="90" step="5" value="0" />
 90
</div>
<div>
fov 10<input id="fovSlider" type="range"
 min="10" max="120" step="5" value="45" />
 120
</div>
<div>
aspect 0.5<input id="aspectSlider" type="range"
 min="0.5" max="2" step="0.1" value="1" />
 2
</div>
<script id="vertex-shader" type="x-shader/x-vertex">

attribute  vec4 vPosition;
attribute  vec4 vColor;
varying vec4 fColor;

uniform vec3 theta;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

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

    <!-- fColor = vColor; -->
    <!-- gl_Position = rz * ry * rx * vPosition; -->
    
    gl_Position = projectionMatrix*modelViewMatrix*vPosition;
    fColor = vColor;
} 
</script>

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

precision mediump float;
   
varying vec4 fColor;

void
main()
{
    gl_FragColor = fColor;
}
</script>

<script src="https://esangel.github.io/WebGL/Common/webgl-utils.js"></script>
<script src="https://esangel.github.io/WebGL/Common/initShaders.js"></script>
<script src="https://esangel.github.io/WebGL/Common/MV.js"></script>
<script type="text/javascript" src="Pyramid.js"></script>

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

<button id= "xButton">Rotate X</button>
<button id= "yButton">Rotate Y</button>
<button id= "zButton">Rotate Z</button>  

Ответ №1:

Существует несколько проблем

  1. <!-- ... --> недопустимые комментарии внутри шейдеров GLSL. Использовать /* ... */ или `//

  2. Код имел размер 4 для позиций при вызове gl.vertexAttribPointer . Это должно было быть 3.

  3. В коде было вызвано 2 переменные theta . Один — число, другой — массив, код пытается использовать их в обоих направлениях. Один из способов избежать этой проблемы — использовать let вместо var . var на данный момент в основном устарел. При let этом вы бы получили сообщение об ошибке. Также поместите "use strict"; в начало вашего JavaScript. Это поможет найти больше ошибок. Я переименовал один из них в theta2

  4. Пирамида находится на расстоянии 4 единиц от вида, но дальняя плоскость усеченного конуса установлена на 3, поэтому пирамида находится за пределами усеченного конуса

  5. Вращение представления поворачивается на 2 для каждого кадра, но вращение в радианах, поэтому 2 составляет примерно 1/3 пути, поэтому оно будет вращаться слишком быстро.

 'use strict';
let canvas;
let gl;

let NumVertices  = 12;

let pointsArray = [];
let colorsArray = [];

let xAxis = 0;
let yAxis = 1;
let zAxis = 2;

let axis = 0;
let theta = [ 0, 0, 0 ];

let near = 0.3;
let far = 10.0;
let radius = 4.0;
let theta2  = 0.0;
let phi    = 0.0;
let dr = 5.0 * Math.PI/180.0;

//var thetaLoc;

let  fovy = 45.0;  // Field-of-view in Y direction angle (in degrees)
let  aspect = 1.0;       // Viewport aspect ratio

let modelViewMatrix, projectionMatrix;
let modelViewMatrixLoc, projectionMatrixLoc;
let eye;
const at = vec3(0.0, 0.0, 0.0);
const up = vec3(0.0, 1.0, 0.0);

var vertices = [
    vec3(0.5, -0.2722, 0.2886),
    vec3(0.0, -0.2722, -0.5773),
    vec3(-0.5, -0.2722, 0.2886),
    vec3(0.5, -0.5443, 0.0)
]

var vertexColors = [
    [ 0.0, 0.0, 0.0, 1.0 ],  // black
    [ 1.0, 0.0, 0.0, 1.0 ],  // red
    [ 1.0, 1.0, 0.0, 1.0 ],  // yellow
    [ 0.0, 1.0, 0.0, 1.0 ]  // green

];

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 );
    
    aspect =  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
    //
    let program = initShaders( gl, "vertex-shader", "fragment-shader" );
    gl.useProgram( program );
    
    colorPyramid();

    let cBuffer = gl.createBuffer();
    gl.bindBuffer( gl.ARRAY_BUFFER, cBuffer);
    gl.bufferData( gl.ARRAY_BUFFER, flatten(colorsArray), gl.STATIC_DRAW );
    
    let vColor = gl.getAttribLocation( program, "vColor" );
    gl.vertexAttribPointer( vColor, 4, gl.FLOAT, false, 0, 0 );
    gl.enableVertexAttribArray( vColor);

    let vBuffer = gl.createBuffer();
    gl.bindBuffer( gl.ARRAY_BUFFER, vBuffer);
    gl.bufferData( gl.ARRAY_BUFFER, flatten(pointsArray), gl.STATIC_DRAW );
    
    let vPosition = gl.getAttribLocation( program, "vPosition" );
    gl.vertexAttribPointer( vPosition, 3, gl.FLOAT, false, 0, 0 );
    gl.enableVertexAttribArray( vPosition );
    
    modelViewMatrixLoc = gl.getUniformLocation( program, "modelViewMatrix" );
    projectionMatrixLoc = gl.getUniformLocation( program, "projectionMatrix" );

// sliders for viewing parameters

    document.getElementById("zFarSlider").onchange = function() {
        far = event.srcElement.value;
    };
    document.getElementById("zNearSlider").onchange = function() {
        near = event.srcElement.value;
    };
    document.getElementById("radiusSlider").onchange = function() {
       radius = event.srcElement.value;
    };
    document.getElementById("thetaSlider").onchange = function() {
        theta2 = event.srcElement.value* Math.PI/180.0;
    };
    document.getElementById("phiSlider").onchange = function() {
        phi = event.srcElement.value* Math.PI/180.0;
    };
    document.getElementById("aspectSlider").onchange = function() {
        aspect = event.srcElement.value;
    };
    document.getElementById("fovSlider").onchange = function() {
        fovy = event.srcElement.value;
    };

    render();
}

function colorPyramid()
{
    quad( 0, 1, 2 ); // bottom
    quad( 1, 0, 3 ); // side0
    quad( 1, 2, 3 ); // side1
    quad( 2, 0, 3 ); // side3

}

function quad(a, b, c) 
{

    let indices = [ a, b, c];
    let ndx = pointsArray.length % vertexColors.length;
    for ( let i = 0; i < indices.length;   i ) {
        pointsArray.push( vertices[indices[i]] );

        colorsArray.push(vertexColors[ndx]);
        
    }
}

function render()
{
    // gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    theta[axis]  = 2.0;
    theta2  = .02;
    // gl.uniform3fv(thetaLoc, theta);

    // gl.drawArrays( gl.TRIANGLES, 0, NumVertices );

    // requestAnimFrame( render );
       gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            
    eye = vec3(radius*Math.sin(theta2)*Math.cos(phi), 
    radius*Math.sin(theta2)*Math.sin(phi), radius*Math.cos(theta2));
    modelViewMatrix = lookAt(eye, at , up);
    projectionMatrix = perspective(fovy, aspect, near, far);

    gl.uniformMatrix4fv( modelViewMatrixLoc, false, flatten(modelViewMatrix) );
    gl.uniformMatrix4fv( projectionMatrixLoc, false, flatten(projectionMatrix) );
            
    gl.drawArrays( gl.TRIANGLES, 0, NumVertices );
    requestAnimFrame(render);
}  
 <div>
zNear .01<input id="zNearSlider" type="range"
 min=".01" max="3" step="0.1" value="0.3" />
 3
</div>
<div>
zFar 3<input id="zFarSlider" type="range"
 min="3" max="10" step="3.0" value="3" />
 10
</div>
<div>
radius 0.05<input id="radiusSlider" type="range"
 min="0.05" max="10" step="0.1" value="4" />
 10
</div>
<div>
theta -90<input id="thetaSlider" type="range"
 min="-90" max="90" step="5" value="0" />
 90
</div>
<div>
phi -90<input id="phiSlider" type="range"
 min="-90" max="90" step="5" value="0" />
 90
</div>
<div>
fov 10<input id="fovSlider" type="range"
 min="10" max="120" step="5" value="45" />
 120
</div>
<div>
aspect 0.5<input id="aspectSlider" type="range"
 min="0.5" max="2" step="0.1" value="1" />
 2
</div>
<script id="vertex-shader" type="x-shader/x-vertex">

attribute  vec4 vPosition;
attribute  vec4 vColor;
varying vec4 fColor;

uniform vec3 theta;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

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

    //<!-- fColor = vColor; -->
    //<!-- gl_Position = rz * ry * rx * vPosition; -->
    
    gl_Position = projectionMatrix*modelViewMatrix*vPosition;
    fColor = vColor;
} 
</script>

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

precision mediump float;
   
varying vec4 fColor;

void
main()
{
    gl_FragColor = fColor;
}
</script>

<script src="https://esangel.github.io/WebGL/Common/webgl-utils.js"></script>
<script src="https://esangel.github.io/WebGL/Common/initShaders.js"></script>
<script src="https://esangel.github.io/WebGL/Common/MV.js"></script>

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

<button id= "xButton">Rotate X</button>
<button id= "yButton">Rotate Y</button>
<button id= "zButton">Rotate Z</button>  

Другие советы

  1. Узнайте, как использовать фрагмент

  2. Сделайте ваш код работоспособным, связавшись с библиотеками, которые вы используете

     <script src="https://esangel.github.io/WebGL/Common/webgl-utils.js"></script>
    <script src="https://esangel.github.io/WebGL/Common/initShaders.js"></script>
    <script src="https://esangel.github.io/WebGL/Common/MV.js"></script>
  
  1. Рассмотрите возможность добавления webgl-lint, чтобы помочь найти ошибки.

  2. Извлеките уроки из нескольких разных уроков.