tutoriales tutorial transformaciones matrices geometricas for beginners opengl 3d glsl raytracing

opengl - tutorial - Raycasting: ¿cómo aplicar correctamente una matriz de proyección?



opengl tutoriales (4)

Actualmente estoy trabajando en algunos raycasting en GLSL que funciona bien. De todos modos, quiero pasar ahora de la proyección ortogonal a la proyección de perspectiva, pero no estoy seguro de cómo hacerlo correctamente. ¿Hay algún buen enlace sobre cómo usar una matriz de proyección con raycasting? Ni siquiera estoy seguro de a qué tengo que aplicar la matriz (¿de algún modo es propagable a la dirección del rayo?). Ahora mismo lo hago así (pseudocódigo):

vec3 rayDir = (0.0, 0.0, -1.0); //down the negative -z axis in parallel;

pero ahora me gustaría usar un projMatrix que funcione de forma similar a la función gluPerspective para que pueda simplemente definir una relación de aspecto, el plano y plano cercano y lejano. Entonces, básicamente, ¿alguien puede proporcionarme un trozo de código para configurar una matriz de gluProjection similar a gluProjection ? Y en segundo lugar dime si es correcto multiplicarlo con rayDirection?


no intentes modificar tus rayos En lugar de hacer esto:

a) crea una matriz usando la ubicación / rotación de tu cámara. b) invierte la matriz c) aplícala a todos los modelos en la escena d) preséntala usando tus métodos normales.

Esta es la forma en que OpenGL también lo hace. Girar la cámara hacia la derecha es lo mismo que girar el mundo hacia la izquierda.


Para filmar rayos en la escena, desea comenzar colocándose (mentalmente) en el mundo después de que se haya aplicado la matriz de proyección. Esto significa que la vista frustrum es ahora una caja de 2x2x1, esto se conoce como volumen de vista canónica. (Las esquinas opuestas de la caja son (-1, -1, 0) y (1, 1, -1).) Los rayos que generes (en el mundo transformado después de la proyección) comenzarán en el origen y golpearán la parte trasera plano de recorte (ubicado en z = -1). El "destino" de su primer rayo debe ser (-1, 1, -1) - la esquina superior izquierda del plano de recorte lejano. (Los "destinos" de rayos subsiguientes se calculan en función de la resolución de su ventana gráfica).

Ahora que tiene este rayo en el volumen de la vista canónica, debe obtenerlo en coordenadas mundiales estándar. ¿Cómo haces esto? Simple: simplemente multiplique por el inverso de la matriz de proyección, a menudo llamada transformación de visualización. Esto colocará sus rayos en el mismo sistema de coordenadas que los objetos en su escena, haciendo que las pruebas de colisión de rayos sean agradables y fáciles.


Respondo esto después de llegar aquí desde una búsqueda en Google.

Las respuestas existentes parecen pasar por alto la falta de comprensión en la pregunta original.

La idea de necesitar aplicar una matriz de proyección cuando raycasting es una tontería

Creamos rayos rayados ortogonales comenzando desde el plano de vista y trazando rayos en la misma dirección para cada píxel. el origen del rayo cambia por píxel

Creamos rayos de perspectiva comenzando en la posición del ojo, detrás del plano de vista y trazando una dirección única para cada píxel. es decir, el origen del rayo es fijo y el mismo para cada píxel.

Comprenda que las matrices de proyección en sí mismas, y el proceso en el que generalmente están involucradas se deriva de la difusión de rayos. La matriz de perspectiva codifica un raycast del tipo que describí.

Proyectar un punto en la pantalla es lanzar un rayo desde el plano de la vista / ojo al punto y encontrar la intersección con el plano de la vista ...


En Perspective Projection, la matriz de proyección describe el mapeo de los puntos 3D en el mundo tal como se ven desde una cámara estenopeica, hasta los puntos 2D de la ventana gráfica.
Las coordenadas del espacio visual en la cámara troncocónica (una pirámide truncada) se asignan a un cubo (las coordenadas del dispositivo normalizado).

La Matriz de Proyección Perspectiva se ve así:

r = right, l = left, b = bottom, t = top, n = near, f = far 2*n/(r-l) 0 0 0 0 2*n/(t-b) 0 0 (r+l)/(r-l) (t+b)/(t-b) -(f+n)/(f-n) -1 0 0 -2*f*n/(f-n) 0

wher:

r = w / h t = tan( fov_y / 2 ); 2 * n / (r-l) = 1 / (t * a) 2 * n / (t-b) = 1 / t

Si la proyección es simétrica, donde la línea de visión está en el centro del puerto de visualización y el campo de visión no se desplaza, entonces la matriz se puede simplificar:

1/(t*a) 0 0 0 0 1/t 0 0 0 0 -(f+n)/(f-n) -1 0 0 -2*f*n/(f-n) 0


La siguiente función calculará la misma matriz de proyección que hace gluPerspective :

#include <array> const float cPI = 3.14159265f; float ToRad( float deg ) { return deg * cPI / 180.0f; } using TVec4 = std::array< float, 4 >; using TMat44 = std::array< TVec4, 4 >; TMat44 Perspective( float fov_y, float aspect ) { float fn = far + near float f_n = far - near; float r = aspect; float t = 1.0f / tan( ToRad( fov_y ) / 2.0f ); return TMat44{ TVec4{ t / r, 0.0f, 0.0f, 0.0f }, TVec4{ 0.0f, t, 0.0f, 0.0f }, TVec4{ 0.0f, 0.0f, -fn / f_n, -1.0f }, TVec4{ 0.0f, 0.0f, -2.0f*far*near / f_n, 0.0f } }; }


Ver más:


Ejemplo de WebGL:

<script type="text/javascript"> camera_vert = "precision mediump float; /n" + "attribute vec3 inPos; /n" + "attribute vec3 inCol; /n" + "varying vec3 vertCol;" + "uniform mat4 u_projectionMat44;" + "uniform mat4 u_viewMat44;" + "uniform mat4 u_modelMat44;" + "void main()" + "{" + " vertCol = inCol;" + " vec4 modolPos = u_modelMat44 * vec4( inPos, 1.0 );" + " vec4 viewPos = u_viewMat44 * modolPos;" + " gl_Position = u_projectionMat44 * viewPos;" + "}"; camera_frag = "precision mediump float; /n" + "varying vec3 vertCol;" + "void main()" + "{" + " gl_FragColor = vec4( vertCol, 1.0 );" + "}"; glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array ); function IdentityMat44() { var a=new glArrayType(16); a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=0;a[5]=1;a[6]=0;a[7]=0;a[8]=0;a[9]=0;a[10]=1;a[11]=0;a[12]=0;a[13]=0;a[14]=0;a[15]=1; return a; }; function Cross( a, b ) { return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0 ]; } function Dot( a, b ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; } function Normalize( v ) { var len = Math.sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] ); return [ v[0] / len, v[1] / len, v[2] / len ]; } var Camera = {}; Camera.create = function() { this.pos = [0, 8, 0.5]; this.target = [0, 0, 0]; this.up = [0, 0, 1]; this.fov_y = 90; this.vp = [800, 600]; this.near = 0.5; this.far = 100.0; } Camera.Perspective = function() { var fn = this.far + this.near; var f_n = this.far - this.near; var r = this.vp[0] / this.vp[1]; var t = 1 / Math.tan( Math.PI * this.fov_y / 360 ); var m = IdentityMat44(); m[0] = t/r; m[1] = 0; m[2] = 0; m[3] = 0; m[4] = 0; m[5] = t; m[6] = 0; m[7] = 0; m[8] = 0; m[9] = 0; m[10] = -fn / f_n; m[11] = -1; m[12] = 0; m[13] = 0; m[14] = -2 * this.far * this.near / f_n; m[15] = 0; return m; } function ToVP( v ) { return [ v[1], v[2], -v[0] ] } Camera.LookAt = function() { var p = ToVP( this.pos ); t = ToVP( this.target ); u = ToVP( this.up ); var mx = Normalize( [ t[0]-p[0], t[1]-p[1], t[2]-p[2] ] ); var my = Normalize( Cross( u, mx ) ); var mz = Normalize( Cross( mx, my ) ); var eyeInv = [ -this.pos[0], -this.pos[1], -this.pos[2] ]; var tx = Dot( eyeInv, [mx[0], my[0], mz[0]] ); var ty = Dot( eyeInv, [mx[1], my[1], mz[1]] ); var tz = Dot( eyeInv, [mx[2], my[2], mz[2]] ); var m = IdentityMat44(); m[0] = mx[0]; m[1] = mx[1]; m[2] = mx[2]; m[3] = 0; m[4] = my[0]; m[5] = my[1]; m[6] = my[2]; m[7] = 0; m[8] = mz[0]; m[9] = mz[1]; m[10] = mz[2]; m[11] = 0; m[12] = tx; m[13] = ty; m[14] = tz; m[15] = 1; return m; } // shader program object var ShaderProgram = {}; ShaderProgram.Create = function( shaderList, uniformNames ) { var shaderObjs = []; for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) { var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage ); if ( shderObj == 0 ) return 0; shaderObjs.push( shderObj ); } if ( !this.LinkProgram( shaderObjs ) ) return 0; this.unifomLocation = {}; for ( var i_n = 0; i_n < uniformNames.length; ++ i_n ) { var name = uniformNames[i_n]; this.unifomLocation[name] = gl.getUniformLocation( this.prog, name ); } return this.prog; } ShaderProgram.Use = function() { gl.useProgram( this.prog ); } ShaderProgram.SetUniformMat44 = function( name, mat ) { gl.uniformMatrix4fv( this.unifomLocation[name], false, mat ); } ShaderProgram.CompileShader = function( source, shaderStage ) { var shaderObj = gl.createShader( shaderStage ); gl.shaderSource( shaderObj, source ); gl.compileShader( shaderObj ); return gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS ) ? shaderObj : 0; } ShaderProgram.LinkProgram = function( shaderObjs ) { this.prog = gl.createProgram(); for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh ) gl.attachShader( this.prog, shaderObjs[i_sh] ); gl.linkProgram( this.prog ); return gl.getProgramParameter( this.prog, gl.LINK_STATUS ) ? true : false; } function drawScene(){ var canvas = document.getElementById( "camera-canvas" ); Camera.create(); Camera.vp = [canvas.width, canvas.height]; var currentTime = Date.now(); var deltaMS = currentTime - startTime; Camera.pos = EllipticalPosition( 7, 4, CalcAng( currentTime, 10.0 ) ); gl.viewport( 0, 0, canvas.width, canvas.height ); gl.enable( gl.DEPTH_TEST ); gl.clearColor( 0.0, 0.0, 0.0, 1.0 ); gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); ShaderProgram.Use(); ShaderProgram.SetUniformMat44( "u_projectionMat44", Camera.Perspective() ); ShaderProgram.SetUniformMat44( "u_viewMat44", Camera.LookAt() ); ShaderProgram.SetUniformMat44( "u_modelMat44", IdentityMat44() ); gl.enableVertexAttribArray( prog.inPos ); gl.bindBuffer( gl.ARRAY_BUFFER, buf.pos ); gl.vertexAttribPointer( prog.inPos, 3, gl.FLOAT, false, 0, 0 ); gl.enableVertexAttribArray( prog.inCol ); gl.bindBuffer( gl.ARRAY_BUFFER, buf.col ); gl.vertexAttribPointer( prog.inCol, 3, gl.FLOAT, false, 0, 0 ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, buf.inx ); gl.drawElements( gl.TRIANGLES, 12, gl.UNSIGNED_SHORT, 0 ); gl.disableVertexAttribArray( buf.pos ); gl.disableVertexAttribArray( buf.col ); } var startTime; function Fract( val ) { return val - Math.trunc( val ); } function CalcAng( currentTime, intervall ) { return Fract( (currentTime - startTime) / (1000*intervall) ) * 2.0 * Math.PI; } function CalcMove( currentTime, intervall, range ) { var pos = self.Fract( (currentTime - startTime) / (1000*intervall) ) * 2.0 var pos = pos < 1.0 ? pos : (2.0-pos) return range[0] + (range[1] - range[0]) * pos; } function EllipticalPosition( a, b, angRag ) { var a_b = a * a - b * b var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b ); var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b ); return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ]; } var gl; var prog; var buf = {}; function cameraStart() { var canvas = document.getElementById( "camera-canvas"); gl = canvas.getContext( "experimental-webgl" ); if ( !gl ) return; prog = ShaderProgram.Create( [ { source : camera_vert, stage : gl.VERTEX_SHADER }, { source : camera_frag, stage : gl.FRAGMENT_SHADER } ], [ "u_projectionMat44", "u_viewMat44", "u_modelMat44"] ); prog.inPos = gl.getAttribLocation( prog, "inPos" ); prog.inCol = gl.getAttribLocation( prog, "inCol" ); if ( prog == 0 ) return; var sin120 = 0.8660254 var pos = [ 0.0, 0.0, 1.0, 0.0, -sin120, -0.5, sin120 * sin120, 0.5 * sin120, -0.5, -sin120 * sin120, 0.5 * sin120, -0.5 ]; var col = [ 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0 ]; var inx = [ 0, 1, 2, 0, 2, 3, 0, 3, 1, 1, 3, 2 ]; buf.pos = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, buf.pos ); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( pos ), gl.STATIC_DRAW ); buf.col = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, buf.col ); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( col ), gl.STATIC_DRAW ); buf.inx = gl.createBuffer(); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, buf.inx ); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( inx ), gl.STATIC_DRAW ); startTime = Date.now(); setInterval(drawScene, 50); } </script> <body onload="cameraStart();"> <canvas id="camera-canvas" style="border: none;" width="512" height="256"></canvas> </body>