2017-12-17 44 views
3

GLSL 빌보드 버텍스 쉐이더 솔루션을 찾고 있습니다. 텍스처가있는 쿼드를 렌더링 중입니다.모델 뷰 프로젝션 빌보드 정점 셰이더에서 회전 및 스케일링 변환을 유지하는 방법은 무엇입니까?

precision mediump float; 

attribute vec3 position; 
attribute vec2 uvs; 

uniform mat4 projection; 
uniform mat4 view; 
uniform mat4 model; 

varying vec2 uv; 

void main() { 
    uv = uvs; 

    gl_Position = projection * view * model * vec4(position, 1); 
} 

내 모델 행렬은 회전, 변환 및 크기 변환 행렬에서 다른 구성된다 : 저는 현재처럼 보이는 버텍스 쉐이더가 있습니다.

내가 시도한 몇 가지 솔루션이 작동하여 빌보드 (카메라 얼굴) 효과를 제공했습니다. 불행히도 원래 회전을 무시하고 변형을 원래 모델 행렬로 크기 조정합니다.

다음

현재 설정 http://requirebin.com/?gist=9491aa294f11b31af639910cfeff7140

쿼드을 회전하는 카메라가의 MVCE은 다음과 같습니다 http://www.geeks3d.com/20140807/billboarding-vertex-shader-glsl/

UPDATE :에서 내가 시도 가장 가까운 솔루션입니다. 쿼드에는 스케일링과 회전이 적용됩니다. 텍스처에 '플레이어 이름'이 표시됩니다. 쿼드는 항상 배율이나 x 회전을 버리지 않고 '플레이어 이름'을 말하는 빌보드로 카메라를 마주보아야합니다.

+2

하드 대답하지만 난의 설정 원점 단위 행렬로보기 *의 model''의 사용을 변경하려고 할 것이다 'view * model' 결과가 갖는 ame 값. 만약 스케일링이 존재한다면 취소하거나 회전시켜야합니다. 그러면 각 단위 벡터 크기를 결과의 크기와 일치하도록 설정해야합니다. 자세한 정보는 [4x4 동질 변환 매트릭스 이해하기] (https://stackoverflow.com/a/)를 참조하십시오. 28084380/2521214) – Spektre

+0

MVCE를 추가했습니다. – kevzettler

+0

아직 문제를 이해하지 못합니다. 'mat4 bbView = mat4 (vec4 (1.0,0.0,0.0,0.0)), vec4 (0.0,1.0,0.0,0.0), vec4 (0.0,0.0,1.0,0.0), view [3])의 잘못된 점은 무엇입니까? 'gl_Position = projection * bbView * model * vec4 (position, 1); ' – Rabbid76

답변

2

개체를 뷰포트에 놓으려면 뷰 행렬의 방향을 생략해야합니다. 방향은 행렬의 정규화 된 왼쪽 위 3 * 3입니다. 모델이 이성을 상실하기 때문에, 당신은 X 축 반전이 보상해야한다 :

precision mediump float; 
attribute vec3 position; 
attribute vec2 uvs; 

uniform mat4 projection; 
uniform mat4 view; 
uniform mat4 model; 

varying vec2 uv; 

void main() { 
    uv = uvs; 

    mat4 bbView = mat4(
     vec4(-1.0,0.0,0.0,0.0), 
     vec4(0.0,1.0,0.0,0.0), 
     vec4(0.0,0.0,1.0,0.0), 
     view[3]); 

    gl_Position = projection * bbView * model * vec4(position, 1); 
} 

주, 변환 행렬 (모델과 뷰 행렬은) 다음과 같습니다

(X-axis.x, X-axis.y, X-axis.z, 0) 
(Y-axis.x, Y-axis.y, Y-axis.z, 0) 
(Z-axis.x, Z-axis.y, Z-axis.z, 0) 
(trans.x, trans.y, trans.z, 1) 

빌보드 행렬 bbView은 뷰 (카메라) 위치를 사용하지만 뷰 매트릭스가 제공하는 회전은 생략합니다. 이로 인해 객체가 정면에서 보듯이 보입니다.


투시 투영에서 대상의 크기는 카메라까지의 거리에 따라 선형 적으로 변경됩니다.

precision mediump float; 
attribute vec3 position; 
attribute vec2 uvs; 

uniform mat4 projection; 
uniform mat4 view; 
uniform mat4 model; 

varying vec2 uv; 

void main() { 
    uv = uvs; 

    float scale = length(view[3].xyz); 
    mat4 scaleMat = mat4(
     vec4(scale,0.0,0.0,0.0), 
     vec4(0.0,scale,0.0,0.0), 
     vec4(0.0,0.0,1.0,0.0), 
     vec4(0.0,0.0,0.0,1.0)); 

    mat4 bbView = mat4(
     vec4(-1.0,0.0,0.0,0.0), 
     vec4(0.0,1.0,0.0,0.0), 
     vec4(0.0,0.0,1.0,0.0), 
     view[3]); 

    gl_Position = projection * bbView * model * scaleMat * vec4(position, 1); 
} 

이 코드를 참조하십시오 : 당신이 개체의 크기와 뷰포트에 그 자리를 유지하는 것이 원하는 경우

의미, 당신은 카메라까지의 거리에 의해 객체의 크기를 조절해야

glArrayType = typeof Float32Array !="undefined" ? Float32Array : (typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array); 
 

 
function IdentityMat44() { 
 
    var m = new glArrayType(16); 
 
    m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 0; 
 
    m[4] = 0; m[5] = 1; m[6] = 0; m[7] = 0; 
 
    m[8] = 0; m[9] = 0; m[10] = 1; m[11] = 0; 
 
    m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1; 
 
    return m; 
 
}; 
 

 
function RotateAxis(matA, angRad, axis) { 
 
    var aMap = [ [1, 2], [2, 0], [0, 1] ]; 
 
    var a0 = aMap[axis][0], a1 = aMap[axis][1]; 
 
    var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad); 
 
    var matB = new glArrayType(16); 
 
    for (var i = 0; i < 16; ++ i) matB[i] = matA[i]; 
 
    for (var i = 0; i < 3; ++ i) { 
 
     matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng; 
 
     matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng; 
 
    } 
 
    return matB; 
 
} 
 

 
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, 3, 0.0]; 
 
    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; 
 
} 
 
Camera.LookAt = function() { 
 
    var mz = Normalize([ this.pos[0]-this.target[0], this.pos[1]-this.target[1], this.pos[2]-this.target[2] ]); 
 
    var mx = Normalize(Cross(this.up, mz)); 
 
    var my = Normalize(Cross(mz, mx)); 
 
    var tx = Dot(mx, this.pos); 
 
    var ty = Dot(my, this.pos); 
 
    var tz = Dot([-mz[0], -mz[1], -mz[2]], this.pos); 
 
    var m = IdentityMat44(); 
 
    m[0] = mx[0]; m[1] = my[0]; m[2] = mz[0]; m[3] = 0; 
 
    m[4] = mx[1]; m[5] = my[1]; m[6] = mz[1]; m[7] = 0; 
 
    m[8] = mx[2]; m[9] = my[2]; m[10] = mz[2]; m[11] = 0; 
 
    m[12] = tx; m[13] = ty; m[14] = tz; m[15] = 1; 
 
    return m; 
 
} 
 

 
var ShaderProgram = {}; 
 
ShaderProgram.Create = function(shaderList) { 
 
    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); 
 
    } 
 
    var progObj = this.LinkProgram(shaderObjs) 
 
    if (progObj != 0) { 
 
     progObj.attribIndex = {}; 
 
     var noOfAttributes = gl.getProgramParameter(progObj, gl.ACTIVE_ATTRIBUTES); 
 
     for (var i_n = 0; i_n < noOfAttributes; ++ i_n) { 
 
      var name = gl.getActiveAttrib(progObj, i_n).name; 
 
      progObj.attribIndex[name] = gl.getAttribLocation(progObj, name); 
 
     } 
 
     progObj.unifomLocation = {}; 
 
     var noOfUniforms = gl.getProgramParameter(progObj, gl.ACTIVE_UNIFORMS); 
 
     for (var i_n = 0; i_n < noOfUniforms; ++ i_n) { 
 
      var name = gl.getActiveUniform(progObj, i_n).name; 
 
      progObj.unifomLocation[name] = gl.getUniformLocation(progObj, name); 
 
     } 
 
    } 
 
    return progObj; 
 
} 
 
ShaderProgram.AttributeIndex = function(progObj, name) { return progObj.attribIndex[name]; } 
 
ShaderProgram.UniformLocation = function(progObj, name) { return progObj.unifomLocation[name]; } 
 
ShaderProgram.Use = function(progObj) { gl.useProgram(progObj); } 
 
ShaderProgram.SetUniformI1 = function(progObj, name, val) { if(progObj.unifomLocation[name]) gl.uniform1i(progObj.unifomLocation[name], val); } 
 
ShaderProgram.SetUniformF1 = function(progObj, name, val) { if(progObj.unifomLocation[name]) gl.uniform1f(progObj.unifomLocation[name], val); } 
 
ShaderProgram.SetUniformM44 = function(progObj, name, mat) { if(progObj.unifomLocation[name]) gl.uniformMatrix4fv(progObj.unifomLocation[name], false, mat); } 
 
ShaderProgram.CompileShader = function(source, shaderStage) { 
 
    var shaderScript = document.getElementById(source); 
 
    if (shaderScript) { 
 
     source = ""; 
 
     var node = shaderScript.firstChild; 
 
     while (node) { 
 
     if (node.nodeType == 3) source += node.textContent; 
 
     node = node.nextSibling; 
 
     } 
 
    } 
 
    var shaderObj = gl.createShader(shaderStage); 
 
    gl.shaderSource(shaderObj, source); 
 
    gl.compileShader(shaderObj); 
 
    var status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS); 
 
    if (!status) alert(gl.getShaderInfoLog(shaderObj)); 
 
    return status ? shaderObj : 0; 
 
} 
 
ShaderProgram.LinkProgram = function(shaderObjs) { 
 
    var prog = gl.createProgram(); 
 
    for (var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh) 
 
     gl.attachShader(prog, shaderObjs[i_sh]); 
 
    gl.linkProgram(prog); 
 
    status = gl.getProgramParameter(prog, gl.LINK_STATUS); 
 
    if (!status) alert("Could not initialise shaders"); 
 
    gl.useProgram(null); 
 
    return status ? prog : 0; 
 
} 
 

 
var VertexBuffer = {}; 
 
VertexBuffer.Create = function(attributes, indices) { 
 
    var buffer = {}; 
 
    buffer.buf = []; 
 
    buffer.attr = [] 
 
    for (var i = 0; i < attributes.length; ++ i) { 
 
     buffer.buf.push(gl.createBuffer()); 
 
     buffer.attr.push({ size : attributes[i].attrSize, loc : attributes[i].attrLoc }); 
 
     gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]); 
 
     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(attributes[i].data), gl.STATIC_DRAW); 
 
    } 
 
    buffer.inx = gl.createBuffer(); 
 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx); 
 
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); 
 
    buffer.inxLen = indices.length; 
 
    gl.bindBuffer(gl.ARRAY_BUFFER, null); 
 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); 
 
    return buffer; 
 
} 
 
VertexBuffer.Draw = function(bufObj) { 
 
    for (var i = 0; i < bufObj.buf.length; ++ i) { 
 
     gl.bindBuffer(gl.ARRAY_BUFFER, bufObj.buf[i]); 
 
     gl.vertexAttribPointer(bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0); 
 
     gl.enableVertexAttribArray(bufObj.attr[i].loc); 
 
    } 
 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx); 
 
    gl.drawElements(gl.TRIANGLES, bufObj.inxLen, gl.UNSIGNED_SHORT, 0); 
 
    for (var i = 0; i < bufObj.buf.length; ++ i) 
 
     gl.disableVertexAttribArray(bufObj.attr[i].loc); 
 
    gl.bindBuffer(gl.ARRAY_BUFFER, null); 
 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); 
 
} 
 
     
 
function drawScene(){ 
 

 
    var canvas = document.getElementById("billboard-canvas"); 
 
    Camera.create(); 
 
    Camera.vp = [canvas.width, canvas.height]; 
 
    var currentTime = Date.now(); 
 
    var deltaMS = currentTime - startTime; 
 
    
 
    var texUnit = 0; 
 
    gl.activeTexture(gl.TEXTURE0 + texUnit); 
 
    gl.bindTexture(gl.TEXTURE_2D, textureObj); 
 

 
    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); 
 
    
 
    var rotMat = IdentityMat44(); 
 
    rotMat = RotateAxis(rotMat, CalcAng(currentTime, 13.0), 0); 
 
    rotMat = RotateAxis(rotMat, CalcAng(currentTime, 17.0), 1); 
 
    var d = 1.0 + 0.7*Math.sin(CalcAng(currentTime, 25.0)) 
 
    Camera.pos = [d*rotMat[0], d*rotMat[1], d*rotMat[2]]; 
 
    var viewMat = Camera.LookAt(); 
 

 
    // set up draw shader 
 
    ShaderProgram.Use(progDraw); 
 
    ShaderProgram.SetUniformM44(progDraw, "u_projectionMat44", Camera.Perspective()); 
 
    ShaderProgram.SetUniformM44(progDraw, "u_viewMat44", viewMat); 
 
    ShaderProgram.SetUniformI1(progDraw, "u_texture", texUnit); 
 
    var modelMat = IdentityMat44(); 
 
    modelMat[0] = 0.5; modelMat[5] = 0.5; 
 
    modelMat[12] = -0.55; 
 
    ShaderProgram.SetUniformM44(progDraw, "u_modelMat44", modelMat); 
 
    ShaderProgram.SetUniformF1(progDraw, "u_billboard", 0.0); 
 
    VertexBuffer.Draw(bufPlane); 
 

 
    modelMat[12] = 0.55 
 
    ShaderProgram.SetUniformM44(progDraw, "u_modelMat44", modelMat); 
 
    ShaderProgram.SetUniformF1(progDraw, "u_billboard", 1.0); 
 
    VertexBuffer.Draw(bufPlane); 
 
} 
 

 
var Texture = {}; 
 
Texture.HandleLoadedTexture2D = function(image, texture, flipY) { 
 
    gl.activeTexture(gl.TEXTURE0); 
 
    gl.bindTexture(gl.TEXTURE_2D, texture); 
 
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); 
 
    if (flipY != undefined && flipY == true) 
 
     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 
 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 
 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 
 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); 
 
    \t gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); 
 
    gl.bindTexture(gl.TEXTURE_2D, null); 
 
    return texture; 
 
} 
 
Texture.LoadTexture2D = function(name) { 
 
    var texture = gl.createTexture(); 
 
    texture.image = new Image(); 
 
    texture.image.setAttribute('crossorigin', 'anonymous'); 
 
    texture.image.onload = function() { 
 
     Texture.HandleLoadedTexture2D(texture.image, texture, true) 
 
    } 
 
    texture.image.src = name; 
 
    return texture; 
 
} 
 

 
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 sliderScale = 100.0 
 
var gl; 
 
var progDraw; 
 
var bufCube = {}; 
 
function sceneStart() { 
 

 
    var canvas = document.getElementById("billboard-canvas"); 
 
    var vp = [canvas.width, canvas.height]; 
 
    gl = canvas.getContext("experimental-webgl"); 
 
    if (!gl) 
 
     return; 
 

 
    progDraw = ShaderProgram.Create( 
 
     [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER }, 
 
     { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER } 
 
     ]); 
 
    progDraw.inPos = gl.getAttribLocation(progDraw, "inPos"); 
 
    progDraw.inTex = gl.getAttribLocation(progDraw, "inTex"); 
 
    if (progDraw == 0) 
 
     return; 
 

 
    var planPosData = [-1.0, -1.0, 0.0,  1.0, -1.0, 0.0,  1.0, 1.0, 0.0, -1.0, 1.0, 0.0]; 
 
    var planTexData = [ 0.0, 1.0,   1.0, 1.0,   1.0, 0.0,   0.0, 0.0  ]; 
 
    var planInxData = [0,1,2,0,2,3]; 
 
    bufPlane = VertexBuffer.Create(
 
    [ { data : planPosData, attrSize : 3, attrLoc : progDraw.inPos }, 
 
     { data : planTexData, attrSize : 2, attrLoc : progDraw.inTex } ], 
 
     planInxData); 
 

 
    textureObj = Texture.LoadTexture2D("https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/tree.jpg"); 
 
    
 
    startTime = Date.now(); 
 
    setInterval(drawScene, 50); 
 
}
<script id="draw-shader-vs" type="x-shader/x-vertex"> 
 
precision mediump float; 
 

 
attribute vec3 inPos; 
 
attribute vec2 inTex; 
 

 
varying vec2 vertTex; 
 

 
uniform mat4 u_projectionMat44; 
 
uniform mat4 u_viewMat44; 
 
uniform mat4 u_modelMat44; 
 

 
uniform float u_billboard; 
 

 
void main() 
 
{ 
 
    vertTex  = inTex; 
 

 
    float scale = u_billboard > 0.5 ? length(u_viewMat44[3].xyz) : 1.0; 
 
    mat4 scaleMat = mat4(
 
     vec4(scale,0.0,0.0,0.0), 
 
     vec4(0.0,scale,0.0,0.0), 
 
     vec4(0.0,0.0,1.0,0.0), 
 
     vec4(0.0,0.0,0.0,1.0)); 
 

 
    mat4 bbView = mat4(
 
     vec4(1.0,0.0,0.0,0.0), 
 
     vec4(0.0,1.0,0.0,0.0), 
 
     vec4(0.0,0.0,1.0,0.0), 
 
     u_viewMat44[3]); 
 

 
    mat4 view = u_billboard > 0.5 ? bbView : u_viewMat44; 
 

 
    gl_Position = u_projectionMat44 * view * scaleMat * u_modelMat44 * vec4(inPos, 1.0); 
 
} 
 
</script> 
 

 
<script id="draw-shader-fs" type="x-shader/x-fragment"> 
 
precision mediump float; 
 

 
varying vec2 vertTex; 
 

 
uniform sampler2D u_texture; 
 
uniform sampler2D u_normalMap; 
 

 
void main() 
 
{ 
 
    vec3 texColor = texture2D(u_texture, vertTex.st).rgb; 
 
    gl_FragColor = vec4(texColor.rgb, 1.0); 
 
} 
 
</script> 
 

 
<body onload="sceneStart();"> 
 
    <canvas id="billboard-canvas" style="border: none;" width="512" height="512"></canvas> 
 
</body>
MCVE없이

+0

두 가지 해결책 모두 예상대로 동작하지 않습니다. 첫 번째 해결책은 쿼드의 월드 위치를 대폭 변경하여 반전/반사 된 Z 위치에서 이동해야합니다. 또한 원래 회전 변환을 삭제합니다. 두 번째 해결 방법은 부적합합니다. "모델 행렬에 의해 수행 된 방향 생략", 질문 제목에서 언급했듯이 모델 행렬 방향을 유지하는 데 필요합니다. – kevzettler

+1

@kevzettler 답변에 코드 스 니펫을 추가했습니다. – Rabbid76

+0

나는 카메라를 줌하거나 빌보드 근처/멀리 카메라를 움직일 때 그것을 알아 차렸다. 고정 된 크기로 머 무르지 않습니다. 빌보드는 원근법으로 비례합니다. 빌보드를 보장하는 어떤 방법이든 거리에 고정 된 규모입니까? – kevzettler