2017-11-22 16 views
3

私はビューボリュームの平面の8(4 + 4)頂点を計算しようとしています。WebGL描写パースペクティブビューボリューム

view volume

私はWebGLの、カメラのビューボリュームで、描画するこの頂点を必要とします。

これまでのところ、各視点から三角法を使って計算することができましたが、頂点を描画すると結果が正確ではないようです。 私はこれまでの頂点に対してこの方程式に到達:

Y = SQRT(斜辺^ 2 - 平面^ 2)

X = SQRT(斜辺^ 2 - 平面^ 2)

Z =面(近くまたは遠い)

誰でも手伝いできますか?前もって感謝します。

答えて

3

逆投影行列を使って標準キューブを投影できます。

const m4 = twgl.m4; 
 
const gl = document.querySelector("canvas").getContext("webgl"); 
 

 
const vs = ` 
 
attribute vec4 position; 
 
uniform mat4 u_worldViewProjection; 
 
void main() { 
 
    gl_Position = u_worldViewProjection * position; 
 
} 
 
`; 
 

 
const fs = ` 
 
precision mediump float; 
 
void main() { 
 
    gl_FragColor = vec4(1, 0, 0, 1); 
 
} 
 
`; 
 

 

 
const programInfo = twgl.createProgramInfo(gl, [vs, fs]); 
 

 
const arrays = { 
 
    position: [ 
 
     -1, 1, -1, 
 
     1, 1, -1, 
 
     1, -1, -1, 
 
     -1, -1, -1, 
 

 
     -1, 1, 1, 
 
     1, 1, 1, 
 
     1, -1, 1, 
 
     -1, -1, 1, 
 
    ], 
 
    indices: [ 
 
     0, 1, 1, 2, 2, 3, 3, 0, 
 
     4, 5, 5, 6, 6, 7, 7, 4, 
 
     0, 4, 1, 5, 2, 6, 3, 7, 
 
    ], 
 
}; 
 
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays); 
 

 
function render(time) { 
 
    time *= 0.001; 
 
    twgl.resizeCanvasToDisplaySize(gl.canvas); 
 
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); 
 

 
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 
 

 
    let projectionToViewWith; 
 
    { 
 
    const fov = 30 * Math.PI/180; 
 
    const aspect = gl.canvas.clientWidth/gl.canvas.clientHeight; 
 
    const zNear = 0.5; 
 
    const zFar = 100; 
 
    projectionToViewWith = m4.perspective(fov, aspect, zNear, zFar); 
 
    } 
 
    let projectionToBeViewed; 
 
    { 
 
    const fov = 30 * Math.PI/180; 
 
    const aspect = gl.canvas.clientWidth/gl.canvas.clientHeight; 
 
    const zNear = 2; 
 
    const zFar = 10; 
 
    projectionToBeViewed = m4.perspective(fov, aspect, zNear, zFar); 
 
    } 
 
    const inverseProjectionToBeViewed = m4.inverse(projectionToBeViewed); 
 
    
 
    const radius = 20; 
 
    const eye = [Math.sin(time) * radius, 4, Math.cos(time) * radius]; 
 
    const target = [0, 0, 0]; 
 
    const up = [0, 1, 0]; 
 
    const camera = m4.lookAt(eye, target, up); 
 
    const view = m4.inverse(camera); 
 

 
    const viewProjection = m4.multiply(projectionToViewWith, view); 
 
    
 
    const worldViewProjection = m4.multiply(
 
     viewProjection, 
 
     inverseProjectionToBeViewed); 
 

 
    gl.useProgram(programInfo.program); 
 
    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); 
 

 
    twgl.setUniforms(programInfo, { 
 
    u_worldViewProjection: worldViewProjection, 
 
    }); 
 
    twgl.drawBufferInfo(gl, bufferInfo, gl.LINES); 
 

 
    requestAnimationFrame(render); 
 
} 
 
requestAnimationFrame(render);
body { margin: 0; } 
 
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script> 
 
<canvas></canvas>

あなただけの逆投影を行う必要がある8つのコーナーのポイントを得るために。投影行列は、-zNearで始まり-zFarで終わり、その空間を透視除算の後に-1 < - > +1ボックスに変換する、高さが高く、fovy *アスペクトの広い錐台の空間をとります。

逆投影して、そのボックスのポイントを計算するには、逆射影行列を使って-1から+1のボックスを投影し、perpective分割をもう一度行います(これはまさに上記の例で起こっていることですSo)をGPUに

をそれをすべてやって、我々はここで例のGPUから引き出しますとJavaScript

[ 
    [-1, 1, -1], 
    [ 1, 1, -1], 
    [ 1, -1, -1], 
    [-1, -1, -1], 

    [-1, 1, 1], 
    [ 1, 1, 1], 
    [ 1, -1, 1], 
    [-1, -1, 1], 
].forEach((point) => { 
    console.log(m4.transformPoint(inverseProjectionMatrix, point)); 
}); 

でそれを行います。

const m4 = twgl.m4; 
 
const gl = document.querySelector("canvas").getContext("webgl"); 
 

 
const vs = ` 
 
attribute vec4 position; 
 
uniform mat4 u_worldViewProjection; 
 
void main() { 
 
    gl_Position = u_worldViewProjection * position; 
 
    gl_PointSize = 10.; 
 
} 
 
`; 
 

 
const fs = ` 
 
precision mediump float; 
 
uniform vec4 u_color; 
 
void main() { 
 
    gl_FragColor = u_color; 
 
} 
 
`; 
 

 

 
const programInfo = twgl.createProgramInfo(gl, [vs, fs]); 
 

 
const positions = [ 
 
    -1, 1, -1, 
 
    1, 1, -1, 
 
    1, -1, -1, 
 
    -1, -1, -1, 
 

 
    -1, 1, 1, 
 
    1, 1, 1, 
 
    1, -1, 1, 
 
    -1, -1, 1, 
 
]; 
 
const arrays = { 
 
    position: positions, 
 
    indices: [ 
 
     0, 1, 1, 2, 2, 3, 3, 0, 
 
     4, 5, 5, 6, 6, 7, 7, 4, 
 
     0, 4, 1, 5, 2, 6, 3, 7, 
 
    ], 
 
}; 
 
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays); 
 

 
function render(time) { 
 
    time *= 0.001; 
 
    twgl.resizeCanvasToDisplaySize(gl.canvas); 
 
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); 
 

 
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 
 

 
    let projectionToViewWith; 
 
    { 
 
    const fov = 30 * Math.PI/180; 
 
    const aspect = gl.canvas.clientWidth/gl.canvas.clientHeight; 
 
    const zNear = 0.5; 
 
    const zFar = 100; 
 
    projectionToViewWith = m4.perspective(fov, aspect, zNear, zFar); 
 
    } 
 
    let projectionToBeViewed; 
 
    { 
 
    const fov = 30 * Math.PI/180; 
 
    const aspect = gl.canvas.clientWidth/gl.canvas.clientHeight; 
 
    const zNear = 2; 
 
    const zFar = 10; 
 
    projectionToBeViewed = m4.perspective(fov, aspect, zNear, zFar); 
 
    } 
 
    const inverseProjectionToBeViewed = m4.inverse(projectionToBeViewed); 
 
    
 
    const radius = 20; 
 
    const eye = [Math.sin(time) * radius, 4, Math.cos(time) * radius]; 
 
    const target = [0, 0, 0]; 
 
    const up = [0, 1, 0]; 
 
    const camera = m4.lookAt(eye, target, up); 
 
    const view = m4.inverse(camera); 
 

 
    const viewProjection = m4.multiply(projectionToViewWith, view); 
 
    
 
    const worldViewProjection = m4.multiply(
 
     viewProjection, 
 
     inverseProjectionToBeViewed); 
 

 
    gl.useProgram(programInfo.program); 
 
    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); 
 

 
    twgl.setUniforms(programInfo, { 
 
    u_worldViewProjection: worldViewProjection, 
 
    u_color: [1, 0, 0, 1], 
 
    }); 
 
    twgl.drawBufferInfo(gl, bufferInfo, gl.LINES); 
 
    
 
    // just because I'm lazy let's draw each point one at a time 
 
    // note: since in our case the frustum is not moving we 
 
    // could have computed these at init time. 
 
    const positionLoc = programInfo.attribSetters.position.location; 
 
    gl.disableVertexAttribArray(positionLoc); 
 

 
    for (let i = 0; i < positions.length; i += 3) { 
 
    const point = positions.slice(i, i + 3); 
 
    const worldPosition = m4.transformPoint(
 
     inverseProjectionToBeViewed, point); 
 
    gl.vertexAttrib3f(positionLoc, ...worldPosition); 
 
    twgl.setUniforms(programInfo, { 
 
     u_color: [0, 1, 0, 1], 
 
     u_worldViewProjection: viewProjection, 
 
    }); 
 
    gl.drawArrays(gl.POINT, 0, 1); 
 
    } 
 

 
    requestAnimationFrame(render); 
 
} 
 
requestAnimationFrame(render);
body { margin: 0; } 
 
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script> 
 
<canvas></canvas>

あなたは別のキャンバス内の1枚のキャンバスでのカメラの視錐台を見せたかったコメントに言及。

canvasWhosFrustumWeWantToRenderのカメラが動いていないことを除けば、それは上で起こっている事実です。代わりに、ちょうど原点に座って、+ Yで-Z軸を見下ろしています。錐台が、それはtehのカメラに相対的であるだけでカメラ行列

const m4 = twgl.m4; 
 
const gl = document.querySelector("canvas").getContext("webgl"); 
 
const ext = gl.getExtension("OES_standard_derivatives"); 
 

 
const vs = ` 
 
attribute vec4 position; 
 
uniform mat4 u_worldViewProjection; 
 
varying vec3 v_position; 
 
void main() { 
 
    gl_Position = u_worldViewProjection * position; 
 
    v_position = position.xyz; // for fake lighting 
 
} 
 
`; 
 

 
const fs = ` 
 
#extension GL_OES_standard_derivatives : enable 
 
precision mediump float; 
 
varying vec3 v_position; 
 
uniform vec4 u_color; 
 
void main() { 
 
    vec3 fdx = dFdx(v_position); 
 
    vec3 fdy = dFdy(v_position); 
 

 
    vec3 n = normalize(cross(fdx,fdy)); 
 
    float l = dot(n, normalize(vec3(1,2,-3))) * .5 + .5; 
 
    gl_FragColor = u_color; 
 
    gl_FragColor.rgb *= l; 
 
} 
 
`; 
 

 

 
const programInfo = twgl.createProgramInfo(gl, [vs, fs]); 
 

 
const arrays = { 
 
    position: [ 
 
     -1, 1, -1, 
 
     1, 1, -1, 
 
     1, -1, -1, 
 
     -1, -1, -1, 
 

 
     -1, 1, 1, 
 
     1, 1, 1, 
 
     1, -1, 1, 
 
     -1, -1, 1, 
 
    ], 
 
    indices: [ 
 
     0, 1, 1, 2, 2, 3, 3, 0, 
 
     4, 5, 5, 6, 6, 7, 7, 4, 
 
     0, 4, 1, 5, 2, 6, 3, 7, 
 
    ], 
 
}; 
 
const concat = twgl.primitives.concatVertices; 
 
const reorient = twgl.primitives.reorientVertices; 
 
const wireCubeBufferInfo = twgl.createBufferInfoFromArrays(gl, arrays); 
 
const solidCubeBufferInfo = twgl.primitives.createCubeBufferInfo(gl, 2); 
 
const cameraBufferInfo = twgl.createBufferInfoFromArrays(gl, 
 
    concat([ 
 
    reorient(twgl.primitives.createCubeVertices(2), 
 
       m4.translation([0, 0, 1])), 
 
    reorient(twgl.primitives.createTruncatedConeVertices(0, 1, 2, 12, 1), 
 
       m4.rotationX(Math.PI * -.5)), 
 
    ]) 
 
); 
 

 
const black = [0, 0, 0, 1]; 
 
const blue = [0, 0, 1, 1]; 
 

 
function drawScene(viewProjection, clearColor) { 
 
    gl.clearColor(...clearColor); 
 
    gl.clear(gl.COLOR_BUFFER_BIT); 
 

 
    const numCubes = 10; 
 
    for (let i = 0; i < numCubes; ++i) { 
 
    const u = i/numCubes; 
 
    let mat = m4.rotationY(u * Math.PI * 2); 
 
    mat = m4.translate(mat, [0, 0, 10]); 
 
    mat = m4.scale(mat, [1, 1 + u * 23 % 1, 1]); 
 
    mat = m4.translate(mat, [0, .5, 0]); 
 
    mat = m4.multiply(viewProjection, mat); 
 
    drawModel(solidCubeBufferInfo, mat, [u, u * 3 % 1, u * 7 % 1,1]); 
 
    } 
 
} 
 

 
function drawModel(bufferInfo, worldViewProjection, color, mode) { 
 
    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); 
 
    twgl.setUniforms(programInfo, { 
 
    u_worldViewProjection: worldViewProjection, 
 
    u_color: color, 
 
    }); 
 
    twgl.drawBufferInfo(gl, bufferInfo, mode); 
 
} 
 

 
function render(time) { 
 
    time *= 0.001; 
 
    twgl.resizeCanvasToDisplaySize(gl.canvas); 
 
    const width = gl.canvas.width; 
 
    const height = gl.canvas.height; 
 
    const halfWidth = width/2; 
 
    gl.viewport(0, 0, width, height); 
 

 
    gl.disable(gl.SCISSOR_TEST); 
 
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 
 
    gl.enable(gl.DEPTH_TEST); 
 

 
    let projectionToViewWith; // the projection on the right 
 
    { 
 
    const fov = 60 * Math.PI/180; 
 
    const aspect = gl.canvas.clientWidth/2/gl.canvas.clientHeight; 
 
    const zNear = 0.5; 
 
    const zFar = 100; 
 
    projectionToViewWith = m4.perspective(fov, aspect, zNear, zFar); 
 
    } 
 
    let projectionToBeViewed; // the projeciton on the left 
 
    { 
 
    const fov = 60 * Math.PI/180; 
 
    const aspect = gl.canvas.clientWidth/2/gl.canvas.clientHeight; 
 
    const zNear = 1.5; 
 
    const zFar = 15; 
 
    projectionToBeViewed = m4.perspective(fov, aspect, zNear, zFar); 
 
    } 
 
    const inverseProjectionToBeViewed = m4.inverse(projectionToBeViewed); 
 
    
 
    let cameraViewingScene; // camera for right view 
 
    { 
 
    const t1 = 0; 
 
    const radius = 30; 
 
    const eye = [Math.sin(t1) * radius, 4, Math.cos(t1) * radius]; 
 
    const target = [0, 0, 0]; 
 
    const up = [0, 1, 0]; 
 
    cameraViewingScene = m4.lookAt(eye, target, up); 
 
    } 
 
    
 
    let cameraInScene; // camera for left view 
 
    { 
 
    const t1 = time; 
 
    const t2 = time + .4; 
 
    const r1 = 10 + Math.sin(t1); 
 
    const r2 = 10 + Math.sin(t2) * 2; 
 
    const eye = [Math.sin(t1) * r1, 0 + Math.sin(t1) * 4, Math.cos(t1) * r1]; 
 
    const target = [Math.sin(t2) * r2, 1 + Math.sin(t2), Math.cos(t2) * r2]; 
 
    const up = [0, 1, 0]; 
 
    cameraInScene = m4.lookAt(eye, target, up); 
 
    } 
 

 
    // there's only one shader program so just set it once 
 
    gl.useProgram(programInfo.program); 
 
    
 
    // draw only on left half of canvas 
 
    gl.enable(gl.SCISSOR_TEST); 
 
    gl.scissor(0, 0, halfWidth, height); 
 
    gl.viewport(0, 0, halfWidth, height); 
 
    
 
    // draw the scene on the left using the camera inside the scene 
 
    { 
 
    const view = m4.inverse(cameraInScene); 
 
    const viewProjection = m4.multiply(projectionToBeViewed, view); 
 
    drawScene(viewProjection, [.9, 1, .9, 1]); 
 
    } 
 
    
 
    // draw only on right half of canvas 
 
    gl.scissor(halfWidth, 0, halfWidth, height); 
 
    gl.viewport(halfWidth, 0, halfWidth, height); 
 
    
 
    // draw the same scene on the right using the camera outside the scene 
 
    { 
 
    const view = m4.inverse(cameraViewingScene); 
 
    const viewProjection = m4.multiply(projectionToViewWith, view); 
 
    drawScene(viewProjection, [.9, 1, 1, 1]); 
 
    
 
    // draw the in scene camera's frustum 
 
    { 
 
     const world = m4.multiply(cameraInScene, inverseProjectionToBeViewed); 
 
     const worldViewProjection = m4.multiply(viewProjection, world); 
 
     drawModel(wireCubeBufferInfo, worldViewProjection, black, gl.LINES); 
 
    } 
 
    
 
    // draw the in scene camera's camera model 
 
    { 
 
     const worldViewProjection = m4.multiply(viewProjection, cameraInScene); 
 
     drawModel(cameraBufferInfo, worldViewProjection, blue); 
 
    } 
 
    } 
 

 
    requestAnimationFrame(render); 
 
} 
 
requestAnimationFrame(render);
body { margin: 0; } 
 
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script> 
 
<canvas></canvas>

+0

うわーで追加する必要がある場所を示すために移動できるようにするには、どうもありがとうございました。それは実際には概念的に簡単です。 私はそれを実装しようとしましたが、私はいくつかの問題を考えています。私は2つのキャンバスを持っています。私はキャンバスに描画したいキャンバスのカメラの錐台です。私は次のことを試しました(私が間違っていれば私を修正してください): あなたが言ったように私は標準のキューブモデルを作成しました。次に、そのモデルの各点Vについて、I *にcanvasBのV * InverseProjectionMatrixを掛けます。それは私にcanvasBのビューボリュームの限界に対応する新しいポイントV "を与えます。私は今すべての正しい点を得て、私はそれらを頂点バッファに送ることができます。これは私に平行六面体のオブジェクトを与えました。 – alejandromumo

+0

あなたはWで割りましたか? 'temp = V * inverseProjectionMatrix; newV = temp.xyz/temp.w' – gman

+0

それは働いた!私は間違った順序で倍増していた。私は十分にあなたに感謝することはできません。 Btwちょっと面白いコメント、私はwebglのチュートリアルに従っています、そして、彼らは金です。 Tyvm! – alejandromumo

関連する問題