【问题标题】:Webgl highmap by displacement mapping according to brightness of textureWebgl highmap 根据纹理的亮度进行置换映射
【发布时间】:2020-04-05 19:44:14
【问题描述】:

我是webgl和opengl的新手,在顶点着色器下面显示错误,只产生一个计划。片段着色器是典型的,没有提供。

uniform mat4 modelview;
uniform mat4 transform;
uniform mat3 normalMatrix;
uniform mat4 texMatrix;
uniform sampler2D texture;
attribute vec4 vertex;
attribute vec4 color;
attribute vec3 normal;
attribute vec2 texCoord;
varying vec4 vertColor;
varying vec4 vertTexCoord;
const float zero_float = 0.0;
const float one_float = 1.0;
const vec3 zero_vec3 = vec3(0);
varying highp float height;


uniform float brightness;

void main() {
  //height =texture2D(texture,vec2(vertex.xz));
  //height =texture2D(texture,vec2(vertex.xz)).r;
  //gl_Position = transform * vertex;
    gl_Position = transform *vec4(vertex.x,vertex.y,brightness,1.0);
  vec3 ecVertex = vec3(modelview * vertex);
  vec3 ecNormal = normalize(normalMatrix * normal);
  vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0);
}  

上述顶点着色器使用纹理图像亮度的置换映射无法显示highmap,并且仅置换一个带有纹理的平面 请帮助顶点如何根据纹理像素的亮度从球体表面(原始形状)移动到更高的位置。(在球体表面上显示山丘,山丘的高度成正比纹理像素的亮度)

【问题讨论】:

    标签: webgl vertex-shader


    【解决方案1】:

    你不能只移动位置

    想象你有一个 2x2 四边形平面

    A--B--C
    | /| /|
    |/ |/ |
    D--E--F
    | /| /|
    |/ |/ |
    G--H--I
    

    点 E 有一个垂直于平面的法线,但如果你突然将点 E 本身垂直于平面移动,则每个使用它的三角形都需要不同的法线,上图中有 6 个三角形。当然,其他顶点的法线也需要改变。

    您需要在片段着色器中使用标准导数来计算新的法线。

    function main() {
      const gl = document.querySelector('canvas').getContext('webgl');
      const ext = gl.getExtension('OES_standard_derivatives');
      if (!ext) {
        return alert('need OES_standard_derivatives');
      }
    
      const m4 = twgl.m4;
      const vs = `
      attribute vec4 position;
      attribute vec2 texcoord;
    
      uniform sampler2D displacementMap;
      uniform mat4 projection;
      uniform mat4 view;
      uniform mat4 model;
    
      varying vec3 v_worldPosition;
    
      void main() {
        float displacementScale = 10.0;
        float displacement = texture2D(displacementMap, texcoord).r * displacementScale;
        vec4 displacedPosition = position + vec4(0, displacement, 0, 0);
        gl_Position = projection * view * model * displacedPosition;
        v_worldPosition = (model * displacedPosition).xyz;
      }  
      `;
    
      const fs = `
      #extension GL_OES_standard_derivatives : enable
    
      precision highp float;
    
      varying vec3 v_worldPosition;
    
      void main() {
        vec3 dx = dFdx(v_worldPosition);
        vec3 dy = dFdy(v_worldPosition);
        vec3 normal = normalize(cross(dy, dx));
        
        // just hard code lightDir and color
        // to make it easy
        vec3 lightDir = normalize(vec3(1, -2, 3));
        float light = dot(lightDir, normal);
        vec3 color = vec3(0.3, 1, 0.1);
        
        gl_FragColor = vec4(color * (light * 0.5 + 0.5), 1);
      }
      `;
    
      // compile shader, link, look up locations
      const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
    
      // make some vertex data
      // calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
      const bufferInfo = twgl.primitives.createPlaneBufferInfo(
          gl,
          96,  // width
          64,  // height
          96,  // quads across
          64,  // quads down
      );
    
      const tex = twgl.createTexture(gl, {
        src: 'https://threejsfundamentals.org/threejs/resources/images/heightmap-96x64.png',
        minMag: gl.NEAREST,
        wrap: gl.CLAMP_TO_EDGE,
      });
    
      function render(time) {
        time *= 0.001;  // seconds
    
        twgl.resizeCanvasToDisplaySize(gl.canvas);
    
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
        gl.enable(gl.DEPTH_TEST);
        gl.enable(gl.CULL_FACE);
    
        const fov = 60 * Math.PI / 180;
        const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
        const near = 0.1;
        const far = 200;
        const projection = m4.perspective(fov, aspect, near, far);
    
        const eye = [Math.cos(time) * 30, 10, Math.sin(time) * 30];
        const target = [0, 0, 0];
        const up = [0, 1, 0];
        const camera = m4.lookAt(eye, target, up);
        const view = m4.inverse(camera);
        const model = m4.identity();
    
        gl.useProgram(programInfo.program);
    
        // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
        twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    
        // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
        twgl.setUniformsAndBindTextures(programInfo, {
          projection,
          view,
          model,
          displacementMap: tex,
        });
    
        // calls gl.drawArrays or gl.drawElements
        twgl.drawBufferInfo(gl, bufferInfo);
    
        requestAnimationFrame(render);
      }
      requestAnimationFrame(render);
    }
    main();
    <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    <canvas id="canvas"></canvas>

    或通过查看置换贴图上的多个点或

    function main() {
      const gl = document.querySelector('canvas').getContext('webgl');
    
      const m4 = twgl.m4;
      const vs = `
      attribute vec4 position;
      attribute vec2 texcoord;
    
      uniform sampler2D displacementMap;
      uniform mat4 projection;
      uniform mat4 view;
      uniform mat4 model;
    
      varying vec2 v_texcoord;
    
      void main() {
        float displacementScale = 10.0;
        float displacement = texture2D(displacementMap, texcoord).r * displacementScale;
        vec4 displacedPosition = position + vec4(0, displacement, 0, 0);
        gl_Position = projection * view * model * displacedPosition;
        v_texcoord = texcoord;
      }  
      `;
    
      const fs = `
      precision highp float;
    
      varying vec2 v_texcoord;
    
      uniform sampler2D displacementMap;
    
      void main() {
        // should make this a uniform so it's shared
        float displacementScale = 10.0;
        
        // I'm sure there is a better way to compute
        // what this offset should be
        float offset = 0.01;
        
        vec2 uv0 = v_texcoord;
        vec2 uv1 = v_texcoord + vec2(offset, 0);
        vec2 uv2 = v_texcoord + vec2(0, offset);
        
        float h0 = texture2D(displacementMap, uv0).r;
        float h1 = texture2D(displacementMap, uv1).r;
        float h2 = texture2D(displacementMap, uv2).r;
        
        vec3 p0 = vec3(uv0, h0 * displacementScale);
        vec3 p1 = vec3(uv1, h1 * displacementScale);
        vec3 p2 = vec3(uv2, h2 * displacementScale);
        
        vec3 v0 = p1 - p0;
        vec3 v1 = p2 - p0;
        
        vec3 normal = normalize(cross(v1, v0));
        
        // just hard code lightDir and color
        // to make it easy
        vec3 lightDir = normalize(vec3(1, -3, 2));
        float light = dot(lightDir, normal);
        vec3 color = vec3(0.3, 1, 0.1);
        
        gl_FragColor = vec4(color * (light * 0.5 + 0.5), 1);
      }
      `;
    
      // compile shader, link, look up locations
      const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
    
      // make some vertex data
      // calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
      const bufferInfo = twgl.primitives.createPlaneBufferInfo(
          gl,
          96,  // width
          64,  // height
          96,  // quads across
          64,  // quads down
      );
    
      const tex = twgl.createTexture(gl, {
        src: 'https://threejsfundamentals.org/threejs/resources/images/heightmap-96x64.png',
        minMag: gl.LINEAR,
        wrap: gl.CLAMP_TO_EDGE,
      });
    
      function render(time) {
        time *= 0.001;  // seconds
    
        twgl.resizeCanvasToDisplaySize(gl.canvas);
    
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
        gl.enable(gl.DEPTH_TEST);
        gl.enable(gl.CULL_FACE);
    
        const fov = 60 * Math.PI / 180;
        const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
        const near = 0.1;
        const far = 200;
        const projection = m4.perspective(fov, aspect, near, far);
    
        const eye = [Math.cos(time) * 30, 10, Math.sin(time) * 30];
        const target = [0, 0, 0];
        const up = [0, 1, 0];
        const camera = m4.lookAt(eye, target, up);
        const view = m4.inverse(camera);
        const model = m4.identity();
    
        gl.useProgram(programInfo.program);
    
        // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
        twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    
        // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
        twgl.setUniformsAndBindTextures(programInfo, {
          projection,
          view,
          model,
          displacementMap: tex,
        });
    
        // calls gl.drawArrays or gl.drawElements
        twgl.drawBufferInfo(gl, bufferInfo);
    
        requestAnimationFrame(render);
      }
      requestAnimationFrame(render);
    }
    main();
    <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    <canvas id="canvas"></canvas>

    请注意,与其从纹理的 3 个样本计算法线,不如在初始化时通过遍历高度图并生成法线图来预先计算它们。您可以将其提供为 3 个相同纹理的通道。比如说 RGB = 正常和 A = 高度

    async function main() {
      const gl = document.querySelector('canvas').getContext('webgl');
      
      const m4 = twgl.m4;
      const v3 = twgl.v3;
      const vs = `
      attribute vec4 position;
      attribute vec2 texcoord;
    
      uniform sampler2D displacementMap;
      uniform mat4 projection;
      uniform mat4 view;
      uniform mat4 model;
    
      varying vec2 v_texcoord;
    
      void main() {
        float displacementScale = 10.0;
        float displacement = texture2D(displacementMap, texcoord).a * displacementScale;
        vec4 displacedPosition = position + vec4(0, displacement, 0, 0);
        gl_Position = projection * view * model * displacedPosition;
        v_texcoord = texcoord;
      }  
      `;
    
      const fs = `
      precision highp float;
    
      varying vec2 v_texcoord;
    
      uniform sampler2D displacementMap;
    
      void main() {
        // should make this a uniform so it's shared
        float displacementScale = 10.0;
        
        vec3 data = texture2D(displacementMap, v_texcoord).rgb;
        vec3 normal = data * 2. - 1.;
        
        // just hard code lightDir and color
        // to make it easy
        vec3 lightDir = normalize(vec3(1, -3, 2));
        float light = dot(lightDir, normal);
        vec3 color = vec3(0.3, 1, 0.1);
        
        gl_FragColor = vec4(color * (light * 0.5 + 0.5), 1);
      }
      `;
    
      // compile shader, link, look up locations
      const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
    
      // make some vertex data
      // calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
      const bufferInfo = twgl.primitives.createPlaneBufferInfo(
          gl,
          96,  // width
          64,  // height
          96,  // quads across
          64,  // quads down
      );
    
      const img = await loadImage('https://threejsfundamentals.org/threejs/resources/images/heightmap-96x64.png');
      
      // get image data
      const ctx = document.createElement('canvas').getContext('2d');
      ctx.canvas.width = img.width;
      ctx.canvas.height = img.height;
      ctx.drawImage(img, 0, 0);
      const imgData = ctx.getImageData(0, 0, img.width, img.height);
      
      // generate normals from height data
      const displacementScale = 10;
      const data = new Uint8Array(imgData.data.length);
      for (let z = 0; z < imgData.height; ++z) {
        for (let x = 0; x < imgData.width; ++x) {
          const off = (z * img.width + x) * 4;
          const h0 = imgData.data[off];
          const h1 = imgData.data[off + 4] || 0;  // being lazy at edge
          const h2 = imgData.data[off + imgData.width * 4] || 0; // being lazy at edge
          const p0 = [x    , h0 * displacementScale / 255, z    ];
          const p1 = [x + 1, h1 * displacementScale / 255, z    ];
          const p2 = [x    , h2 * displacementScale / 255, z + 1];
          const v0 = v3.normalize(v3.subtract(p1, p0));
          const v1 = v3.normalize(v3.subtract(p2, p0));
          const normal = v3.normalize(v3.cross(v0, v1));
          data[off + 0] = (normal[0] * 0.5 + 0.5) * 255;
          data[off + 1] = (normal[1] * 0.5 + 0.5) * 255;
          data[off + 2] = (normal[2] * 0.5 + 0.5) * 255;
          data[off + 3] = h0;
        }
      }  
    
      const tex = twgl.createTexture(gl, {
        src: data,
        width: imgData.width,
        minMag: gl.LINEAR,
        wrap: gl.CLAMP_TO_EDGE,
      });
    
      function render(time) {
        time *= 0.001;  // seconds
    
        twgl.resizeCanvasToDisplaySize(gl.canvas);
    
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
        gl.enable(gl.DEPTH_TEST);
        gl.enable(gl.CULL_FACE);
    
        const fov = 60 * Math.PI / 180;
        const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
        const near = 0.1;
        const far = 200;
        const projection = m4.perspective(fov, aspect, near, far);
    
        const eye = [Math.cos(time) * 30, 10, Math.sin(time) * 30];
        const target = [0, 0, 0];
        const up = [0, 1, 0];
        const camera = m4.lookAt(eye, target, up);
        const view = m4.inverse(camera);
        const model = m4.identity();
    
        gl.useProgram(programInfo.program);
    
        // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
        twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    
        // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
        twgl.setUniformsAndBindTextures(programInfo, {
          projection,
          view,
          model,
          displacementMap: tex,
        });
    
        // calls gl.drawArrays or gl.drawElements
        twgl.drawBufferInfo(gl, bufferInfo);
    
        requestAnimationFrame(render);
      }
      requestAnimationFrame(render);
    }
    
    function loadImage(url) {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = _ => resolve(img);
        img.onerror = reject;
        img.crossOrigin = 'anonymous';
        img.src = url;
      });
    }
    
    main();
    <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    <canvas id="canvas"></canvas>

    【讨论】:

      猜你喜欢
      • 2014-12-12
      • 2014-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多