2017-12-17 44 views
3

Ich bin auf der Suche nach einer GLSL Billboard Vertex Shader Lösung. Ich rendere ein Quad mit einer Textur darauf. Im Moment habe ich einen Vertex-Shader, die wie folgt aussehen: Matrix an anderer Stelle von einer Rotation, Translation und Skalierung Transformationsmatrizen aufgebaut istWie kann man die Drehung und Skalierung in einem Billboard-Vertex-Shader für die Modellansichtsprojektion beibehalten?

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

Mein Modell.

Ein paar der Lösungen, die ich ausprobiert habe, haben gearbeitet und eine Plakatwand (Gesicht der Kamera) zur Verfügung gestellt. Leider verwerfen sie die ursprüngliche Rotation und Skalierungstransformationen in die ursprüngliche Modellmatrix. Die nächste Lösung, die ich versucht habe, ist aus: http://www.geeks3d.com/20140807/billboarding-vertex-shader-glsl/

UPDATE:

hier ein MVCE des aktuellen Setup ist http://requirebin.com/?gist=9491aa294f11b31af639910cfeff7140

Es ist eine Kamera, ein Quad zu drehen. Auf den Quad sind Skalierung und Rotation angewendet. Die Textur sagt "Spielername". Der Quad sollte immer der Kamera als Reklametafel mit dem Namen "Spielername" zugewandt sein, ohne die Skala oder X-Rotation zu verwerfen.

+2

ohne MCVE schwer zu sagen, aber ich würde versuchen, die Verwendung von 'view * Modell 'mit Einheitsmatrix mit Ursprung in dem s eingestellt zu ändern als das 'view * model' Ergebnis hat. Das sollte abgebrochen oder gedreht werden, wenn Skalierung vorhanden ist, dann müssen Sie jede Einheitsvektorgröße so einstellen, dass sie der Größe aus dem Ergebnis entspricht ... Weitere Informationen finden Sie unter [Verständnis von homogenen 4x4-Transformationsmatrizen] (https://stackoverflow.com/a/ 28084380/2521214) – Spektre

+0

Ich habe eine MVCE hinzugefügt – kevzettler

+0

Ich verstehe immer noch nicht das Problem. Was stimmt nicht mit '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), Ansicht [3]);' 'gl_Position = Projektion * bbView * Modell * vec4 (Position, 1);' – Rabbid76

Antwort

2

Um das Objekt auf das Ansichtsfenster auszurichten, müssen Sie die Ausrichtung der Ansichtsmatrix weglassen. Die Orientierung ist die normalisierte obere linke 3 * 3 der Matrix. Da Ihr Modell umgedreht wird, müssen Sie dies kompensieren, indem die X-Achse invertieren:

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

Hinweis, eine Transformationsmatrix (Modell und Ansicht Matrix) sieht wie folgt aus:

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

der Billboard-Matrix bbView verwendet die Ansichtsposition (Kamera), lässt jedoch die von der Ansichtsmatrix bereitgestellte Drehung aus. Dadurch sieht das Objekt so aus, als ob es von vorne betrachtet wird.


Bei der perspektivischen Projektion ändert sich die Größe eines Objekts linear mit der Entfernung zur Kamera.

Das bedeutet, wenn Sie wollen, dass das Objekt seine Größe und seinen Platz auf dem Darstellungsfeld hält, dann haben Sie durch den Abstand zur Kamera um das Objekt zu skalieren:

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

den Code-Snippet Siehe:

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>

+0

. Keine der Lösungen hat das erwartete Verhalten. Die erste Lösung ändert drastisch die Weltposition des Quads und bewegt es in eine inverse/reflektierte Z-Position von wo es sein sollte. Es verwirft auch die ursprüngliche Rotationstransformation. die 2. Lösung ist irrelevant. "Lassen Sie die Orientierung weg, die von der Modellmatrix ausgeführt wird", wie im Fragetitel angegeben, ist es erforderlich, die Modellmatrixausrichtung beizubehalten – kevzettler

+1

@kevzettler Ich habe der Antwort ein Code-Snippet hinzugefügt. – Rabbid76

+0

Mir ist aufgefallen, dass wenn ich die Kamera zoome, oder die Kamera nahe/weit zur Plakatwand schiebe. Es bleibt nicht in einer festen Größe. Die Plakatwand skaliert mit der Perspektive. Wie kann man sicherstellen, dass die Werbetafel in Entfernungen eine feste Größe hat? – kevzettler

Verwandte Themen