Как я могу деформировать матрицу шейдера, чтобы соответствовать изометрической перспективе в 3D-сцене? - PullRequest
1 голос
/ 12 мая 2019

Я применяю шейдер в качестве текстуры к плоскости в изометрической сцене.Плоскость лежит плоско с размерами x, z.У меня возникли проблемы с настройкой шаблона шейдера в соответствии с изометрической перспективой, в которой находится сцена.

Вот пример, где шейдер вращается с плоскостью (как обычная текстура), передавая ориентацию как форму.

Вот "2d" (орфографическая) проекция текстуры шейдера:

var TWO_PI = Math.PI * 2;
var PI = Math.PI;

var width = window.innerHeight - 50;
var height = window.innerHeight - 50;
var aspect = width / height;
var planeSize = width * 0.75;

var clock = new THREE.Clock();

var camera, scene, renderer;
var plane, geom_plane, mat_plane;

function init() {

  // ---------- scene 

  scene = new THREE.Scene();

  // ---------- plane

  var plane_w = planeSize;
  var plane_h = planeSize;

  var geom_plane = new THREE.PlaneGeometry(plane_w,
    plane_h,
    0);
  var mat_plane = new THREE.MeshBasicMaterial({
    color: 0xffff00,
    side: THREE.DoubleSide
  });

  var shaderMaterial_plane = new THREE.ShaderMaterial({
    uniforms: {
      u_resolution: {
        value: new THREE.Vector2(planeSize, planeSize)
      },
      u_rotation_x: {
        value: performance.now() * 0.001
      },
      u_rotation_y: {
        value: performance.now() * 0.001
      }
    },
    vertexShader: document.getElementById('vertexshader').textContent,
    fragmentShader: document.getElementById('fragmentshader').textContent,
    blending: THREE.NormalBlending,
    depthTest: true,
    transparent: true
  });

  plane = new THREE.Mesh(geom_plane, shaderMaterial_plane);
  scene.add(plane);

  // ---------- cam

  camera = new THREE.OrthographicCamera(width / -2, width / 2, height / 2, height / -2, 1, 5000);
  camera.position.set(0, 0, planeSize);
  camera.lookAt(scene.position);

  // ---------- renderer

  renderer = new THREE.WebGLRenderer({
    antialias: false,
    alpha: true
  });
  renderer.setSize(width, height);
  renderer.setClearColor(0x000000);
  document.body.appendChild(renderer.domElement);
}

function animate() {
  requestAnimationFrame(animate);
  var time = performance.now() * 0.001;

  plane.material.uniforms.u_rotation_x.value = Math.sin(time * 0.2);
  plane.material.uniforms.u_rotation_y.value = Math.cos(time * 0.2);

  var delta = clock.getDelta();
  render();
}

function render() {
  renderer.render(scene, camera);
}

init();
animate();
<script type="x-shader/x-vertex" id="vertexshader">
varying vec2 vUv;
void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">

    uniform vec2 u_resolution;  // Canvas size (width,height)
    uniform float u_rotation_x;
    uniform float u_rotation_y;

    mat2 rotate2d(vec2 _angles){
        return mat2(_angles.x,
                    -_angles.x,
                    _angles.y,
                    _angles.y);
    }

    float map(float value, float min1, float max1, float min2, float max2) {
        return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
    }

    void main() {
        vec2 st = gl_FragCoord.xy/u_resolution.xy;
        vec3 color = vec3(1.0,1.0,1.0);
        float gradientLength = 0.2;
        float t = 18.;

        // move matrix in order to set rotation pivot point to center
        st -= vec2(0.5);

        // rotate
        vec2 u_rotation = vec2(u_rotation_x, u_rotation_y);
        st = rotate2d(u_rotation) * st;

        // move matrix back
        st += vec2(0.5);

        // apply gradient pattern
        vec2 p = vec2(floor(gl_FragCoord.x), floor(gl_FragCoord.y));
        float pp = clamp(gl_FragCoord.y,-0.5,st.y);
        float val = mod((pp + t), gradientLength);
        float alpha = map(val, 0.0, gradientLength, 1.0, 0.0);


        gl_FragColor = vec4(color,alpha);
    }
</script>
<div id="threejs_canvas"></div>
<script src="https://threejs.org/build/three.min.js"></script>

И вот оно на плоскости в изометрическом пространстве (с таким же вращением):

var TWO_PI = Math.PI * 2;
var PI = Math.PI;

var width = window.innerHeight - 50;
var height = window.innerHeight - 50;
var aspect = width / height;
var canvasCubeSize = width;

var clock = new THREE.Clock();

var camera, scene, renderer;
var wire_cube;
var plane, geom_plane, mat_plane;

function init() {

    // ---------- scene 

    scene = new THREE.Scene();

    // ---------- wire cube 

    var wire_geometry = new THREE.BoxGeometry(canvasCubeSize / 2, canvasCubeSize / 2, canvasCubeSize / 2);
    var wire_material = new THREE.MeshBasicMaterial({
        wireframe: true,
        color: 0xff0000
    });

    wire_cube = new THREE.Mesh(wire_geometry, wire_material);
    scene.add(wire_cube);

    // ---------- plane

    var plane_w = canvasCubeSize / 2;
    var plane_h = plane_w;

    var geom_plane = new THREE.PlaneGeometry(plane_w,
        plane_h,
        0);
    var mat_plane = new THREE.MeshBasicMaterial({
        color: 0xffff00,
        side: THREE.DoubleSide
    });

    var shaderMaterial_plane = new THREE.ShaderMaterial({
        uniforms: {
            u_time: {
                value: 1.0
            },
            u_resolution: {
                value: new THREE.Vector2(canvasCubeSize, canvasCubeSize)
            },
            u_rotation_x: {
                value: wire_cube.rotation.y
            },
            u_rotation_y: {
                value: wire_cube.rotation.y
            }
        },
        vertexShader: document.getElementById('vertexshader').textContent,
        fragmentShader: document.getElementById('fragmentshader').textContent,
        blending: THREE.NormalBlending,
        depthTest: true,
        transparent: true
    });

    plane = new THREE.Mesh(geom_plane, shaderMaterial_plane);
    plane.rotation.x = -PI / 2;
    wire_cube.add(plane);

    // ---------- cam

    camera = new THREE.OrthographicCamera(width / -2, width / 2, height / 2, height / -2, 1, 5000);
    camera.position.set(canvasCubeSize, canvasCubeSize, canvasCubeSize);
    camera.lookAt(scene.position);

    // ---------- renderer 
    renderer = new THREE.WebGLRenderer({
        antialias: false,
        alpha: true
    });
    renderer.setSize(width, height);
    renderer.setClearColor(0x000000);
    document.body.appendChild(renderer.domElement);
}

function animate() {
    
    requestAnimationFrame(animate);

    var time = performance.now() * 0.001;
    wire_cube.rotation.y = time * 0.2;
    if (wire_cube.rotation.y >= TWO_PI) {
        wire_cube.rotation.y -= TWO_PI;
    }

    plane.material.uniforms.u_time.value = time * 0.005;
    plane.material.uniforms.u_rotation_x.value = Math.sin(wire_cube.rotation.y);
    plane.material.uniforms.u_rotation_y.value = Math.cos(wire_cube.rotation.y);

    var delta = clock.getDelta();
    render();
}

function render() {
    renderer.render(scene, camera);
}

init();
animate();
<script type="x-shader/x-vertex" id="vertexshader">
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
</script>
<script type="x-shader/x-fragment" id="fragmentshader">

      uniform vec2 u_resolution;  // Canvas size (width,height)
      uniform float u_rotation_x;
      uniform float u_rotation_y;

      mat2 rotate2d(vec2 _angles){
          return mat2(_angles.x,
                      -_angles.x,
                      _angles.y,
                      _angles.y);
      }

      float map(float value, float min1, float max1, float min2, float max2) {
          return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
      }

      void main() {
          vec2 st = gl_FragCoord.xy/u_resolution.xy;
          vec3 color = vec3(1.0,1.0,1.0);
          float gradientLength = 0.2;
          float t = 18.;

          // move matrix in order to set rotation pivot point to center
          st -= vec2(0.5);

          // rotate
          vec2 u_rotation = vec2(u_rotation_x, u_rotation_y);
          st = rotate2d(u_rotation) * st;

          // move matrix back
          st += vec2(0.5);

          // apply gradient pattern
          vec2 p = vec2(floor(gl_FragCoord.x), floor(gl_FragCoord.y));
          float pp = clamp(gl_FragCoord.y,-0.5,st.y);
          float val = mod((pp + t), gradientLength);
          float alpha = map(val, 0.0, gradientLength, 1.0, 0.0);


          gl_FragColor = vec4(color,alpha);
      }
  </script>
<div id="threejs_canvas">
</div>
<script src="https://threejs.org/build/three.min.js"></script>

если вывод фрагмента слишком мал, см. Здесь

Вращение иллюстрирует, как шейдерне подражает изометрической перспективе.Обратите внимание, что шаблон шейдера не остается фиксированным относительно углов плоскости при их вращении.

Вот фрагментный шейдер:

uniform vec2 u_resolution;  // canvas size (width,height)
    uniform float u_rotation_x;
    uniform float u_rotation_y;

    mat2 rotate2d(vec2 _angles){
        return mat2(_angles.x,
                    -_angles.x,
                    _angles.y,
                    _angles.y);
    }

    float map(float value, float min1, float max1, float min2, float max2) {
        return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
    }

    void main() {
        vec2 st = gl_FragCoord.xy/u_resolution.xy;
        vec3 color = vec3(1.0,1.0,1.0);
        float gradientLength = 0.2;
        float t = 18.;

        // move matrix in order to set rotation pivot point to center
        st -= vec2(0.5);

        // rotate
        vec2 u_rotation = vec2(u_rotation_x, u_rotation_y);
        st = rotate2d(u_rotation) * st;

        // move matrix back
        st += vec2(0.5);

        // apply gradient pattern
        vec2 p = vec2(floor(gl_FragCoord.x), floor(gl_FragCoord.y));
        float pp = clamp(gl_FragCoord.y,-0.5,st.y);
        float val = mod((pp + t), gradientLength);
        float alpha = map(val, 0.0, gradientLength, 1.0, 0.0);


        gl_FragColor = vec4(color,alpha);
    }

Может кто-нибудь помочь мне понять, какдеформировать матрицу в шейдере так, чтобы при вращении она имитировала вращение плоской плоскости в изометрическом пространстве?


Редактировать: Мне интересно, деформируется ли матрица и применяется ли точнаяротация должна быть разбита на две отдельные проблемы?Я играю с изменением скорости вращения на основе ориентации от 0 до TWO_PI, но, возможно, это решение, характерное для этого примера ...

1 Ответ

1 голос
/ 12 мая 2019

Очень интересная задача (+1 за это).Как насчет преобразования единичного круга в эллипс и использования вписанных в него 90-градусных смещенных базисных векторов?

ellipse inscribed basis vectors

Игнорирование математики матрицы здесь GL / GLSL /C ++ пример:

Розыгрыш на стороне процессора:

// GLSL Isometric view
float pan[2]={0.5,0.5};
float u[2]={1.0,0.0};
float v[2]={0.5,0.5};
const float deg=M_PI/180.0;
const float da=1.0*deg;;
static float a=0.0;

u[0]=1.0*cos(a);
u[1]=0.5*sin(a);
v[0]=1.0*cos(a+90.0*deg);
v[1]=0.5*sin(a+90.0*deg);
a+=da; if (a>=2.0*M_PI) a-=2.0*M_PI;

glUseProgram(prog_id);
id=glGetUniformLocation(prog_id,"zoom"); glUniform1f(id,0.5);
id=glGetUniformLocation(prog_id,"pan"); glUniform2fv(id,1,pan);
id=glGetUniformLocation(prog_id,"u"); glUniform2fv(id,1,u);
id=glGetUniformLocation(prog_id,"v"); glUniform2fv(id,1,v);

glBegin(GL_QUADS);
glColor3f(1,1,1);
float x=0.0,y=0.0;
glVertex2f(x+0.0,y+0.0);
glVertex2f(x+0.0,y+1.0);
glVertex2f(x+1.0,y+1.0);
glVertex2f(x+1.0,y+0.0);
glEnd();
glUseProgram(0);

Вершина:

#version 120
// Vertex
uniform vec2 pan=vec2(0.5,0.5); // origin [grid cells]
uniform float zoom=0.5;         // scale
uniform vec2 u=vec2(1.0,0.0);   // basis vectors
uniform vec2 v=vec2(0.5,0.5);
varying vec2 pos;               // position [grid cells]
void main()
    {
    pos=gl_Vertex.xy;
    vec2 a=zoom*(gl_Vertex.xy-pan);
    gl_Position=vec4((u*a.x)+(v*a.y),0.0,1.0);
    }

Фрагмент:

#version 120
// Fragment
varying vec2 pos;               // texture coordinate

void main()
    {
    float a;
    a=2.0*(pos.x+pos.y);
    a-=floor(a);
    gl_FragColor=vec4(a,a,a,1.0);
    }

И, наконец, предварительный просмотр:

preview

Важные вещив вершинный шейдер .Поэтому просто используйте u,v базисные векторы для преобразования из world 2D в Изометрическое 2D положение просто по формуле:

isometric = world.x*u + world.y*v

Остальное просто pan иzoom ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...