同一页面上的多个 WebGL 模型 [英] Multiple WebGL models on the same page

查看:25
本文介绍了同一页面上的多个 WebGL 模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的研究实验室正在开发一个网页,该网页显示一长串可滚动的 3d 模型列表,大约有 50 个左右.我们的第一个想法是使用单独的 THREE.js WebGL 上下文来做到这一点,但鉴于 WebGL 的架构,这似乎是不可取的,而且浏览器似乎将页面上的上下文数量限制为大约 2^4.

我不需要这些上下文来做任何令人印象深刻的事情:单个几何图形只有几百个三角形,没有纹理,并且一次只有一个在使用鼠标旋转其相机时动画.

我可以说服 WebGL 以浏览器不会抱怨的方式做我想做的事吗?我想也许有一个单一的大几何体,我所有的模型都排成一排,并且单独的画布带有视口,每个画布只显示一个模型.但似乎不受支持.(在同一上下文中允许多个视图,但这对我来说不是很有用.)

感谢您的任何想法!

解决方案

不清楚为什么您认为需要多个 webgl 上下文.我猜是因为你想要这样的列表

1.[img] 描述描述2. [img] 说明描述3.【图片】说明描述

或者什么?

一些想法

  1. 为屏幕制作一个足够大的画布,设置它的 CSS 使其不会随着页面的其余部分滚动.绘制与您想要滚动的任何其他 HTML 对齐的模型.

  2. 制作一个离屏 webgl 画布并使用 canvas2d 元素进行显示.

    为每个模型渲染模型然后调用

    someCanvas2DContextForElementN.drawImage(webGLcanvasElement, ...);

鉴于可能只有几个画布可见,您只需要更新那些画布.事实上,回收它们可能是个好主意.换句话说,与其制作 12000 个画布或 12000 个元素列表,不如让它们足以适应屏幕并在滚动时更新它们.

如果我的页面设计允许的话,我个人可能会选择#1.似乎有效,见下文.

<小时>

事实证明这真的很容易.我只是拿了这个绘制 100 个对象的样本,让它一次绘制一个对象.

清屏后开启剪刀测试

gl.enable(gl.SCISSOR_TEST);

然后,对于每个对象

//获取作为我们想要位置的占位符的元素//绘制对象var viewElement = obj.viewElement;//获取它相对于页面视口的位置var rect = viewElement.getBoundingClientRect();//检查它是否在屏幕外.如果是这样跳过它if (rect.bottom < 0 || rect.top > gl.canvas.clientHeight ||rect.right <0 ||rect.left >gl.canvas.clientWidth) {返回;//它在屏幕外}//设置视口var 宽度 = rect.right - rect.left;var 高度 = rect.bottom - rect.top;var left = rect.left;var 底部 = gl.canvas.clientHeight - rect.bottom - 1;gl.viewport(左,底部,宽度,高度);gl.scissor(左、底、宽、高);

我不是 100% 确定是否需要将宽度和高度加 1.我想我应该查一下.

在任何情况下,我都会为每个渲染对象计算一个新的投影矩阵,只是为了使代码通用.占位符 div 的大小可以不同.

更新:

最初发布在这里的解决方案在画布上使用了 position: fixed 以防止其滚动.新的解决方案使用 position: absolute 并在像这样渲染之前更新变换

 gl.canvas.style.transform = `translateY(${window.scrollY}px)`;

使用之前的解决方案,在匹配位置重新绘制的形状可能会滞后于滚动.使用新的解决方案,画布会滚动,直到我们有时间更新它.这意味着如果我们不能足够快地绘制,形状可能会丢失几帧,但它看起来比滚动不匹配要好得多.

以下示例是更新后的解决方案.

"use strict";//使用 twgl.js 因为我很懒twgl.setAttributePrefix("a_");var m4 = twgl.m4;var gl = twgl.getWebGLContext(document.getElementById("c"));//编译着色器,链接程序,查找位置var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);//为每个形状调用 gl.creatBuffer、gl.bindBuffer、gl.bufferData//用于位置、法线、texcoordsvar 形状 = [twgl.primitives.createCubeBufferInfo(gl, 2),twgl.primitives.createSphereBufferInfo(gl, 1, 24, 12),twgl.primitives.createPlaneBufferInfo(gl, 2, 2),twgl.primitives.createTruncatedConeBufferInfo(gl, 1, 0, 2, 24, 1),twgl.primitives.createCresentBufferInfo(gl, 1, 1, 0.5, 0.1, 24),twgl.primitives.createCylinderBufferInfo(gl, 1, 2, 24, 2),twgl.primitives.createDiscBufferInfo(gl, 1, 24),twgl.primitives.createTorusBufferInfo(gl, 1, 0.4, 24, 12),];函数兰特(最小,最大){return min + Math.random() * (max - min);}//共同的价值观var lightWorldPosition = [1, 8, -10];var lightColor = [1, 1, 1, 1];var 相机 = m4.identity();var view = m4.identity();var viewProjection = m4.identity();var tex = twgl.createTexture(gl, {分钟:gl.NEAREST,mag:gl.NEAREST,源代码:[255, 255, 255, 255,192, 192, 192, 255,192, 192, 192, 255,255, 255, 255, 255,],});var randColor = 函数(){var color = [Math.random(), Math.random(), Math.random(), 1];颜色[Math.random() * 3 |0] = 1;//使至少 1 个明亮返回颜色;};var 对象 = [];var numObjects = 100;var list = document.getElementById("list");var listItemTemplate = document.getElementById("list-item-template").text;for (var ii = 0; ii < numObjects; ++ii) {var listElement = document.createElement("div");listElement.innerHTML = listItemTemplate;listElement.className = "list-item";var viewElement = listElement.querySelector(".view");var 制服 = {u_lightWorldPos:lightWorldPosition,u_lightColor: 光色,u_diffuseMult: randColor(),u_specular: [1, 1, 1, 1],u_shininess: 50,u_specularFactor: 1,u_diffuse: tex,u_viewInverse:相机,u_world: m4.identity(),u_worldInverseTranspose: m4.identity(),u_worldViewProjection: m4.identity(),};对象.push({ySpeed: rand(0.1, 0.3),zSpeed: rand(0.1, 0.3),制服:制服,视图元素:视图元素,程序信息:程序信息,缓冲区信息:形状 [ii % 形状.长度],});list.appendChild(listElement);}var showRenderingArea = false;函数渲染(时间){时间 *= 0.001;twgl.resizeCanvasToDisplaySize(gl.canvas);gl.canvas.style.transform = `translateY(${window.scrollY}px)`;gl.启用(gl.DEPTH_TEST);gl.disable(gl.SCISSOR_TEST);gl.clearColor(0, 0, 0, 0);gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);gl.启用(gl.SCISOR_TEST);如果(showRenderingArea){gl.clearColor(0, 0, 1, 1);}var 眼睛 = [0, 0, -8];var 目标 = [0, 0, 0];变量向上 = [0, 1, 0];m4.lookAt(眼睛,目标,向上,相机);m4.inverse(相机,视图);objects.forEach(function(obj, ndx) {var viewElement = obj.viewElement;//获取viewElement的位置var rect = viewElement.getBoundingClientRect();if (rect.bottom < 0 || rect.top > gl.canvas.clientHeight ||rect.right <0 ||rect.left >gl.canvas.clientWidth) {返回;//它在屏幕外}var 宽度 = rect.right - rect.left;var 高度 = rect.bottom - rect.top;var left = rect.left;var 底部 = gl.canvas.clientHeight - rect.bottom - 1;gl.viewport(左,底部,宽度,高度);gl.scissor(左、底、宽、高);如果(showRenderingArea){gl.clear(gl.COLOR_BUFFER_BIT);}var 投影 = m4.perspective(30 * Math.PI/180, 宽/高, 0.5, 100);m4.multiply(投影,视图,视图投影);var uni = obj.uniforms;var world = uni.u_world;m4.identity(世界);m4.rotateY(世界,时间* obj.ySpeed,世界);m4.rotateZ(world, time * obj.zSpeed, world);m4.transpose(m4.inverse(world, uni.u_worldInverseTranspose), uni.u_worldInverseTranspose);m4.multiply(viewProjection, uni.u_world, uni.u_worldViewProjection);gl.useProgram(obj.programInfo.program);//调用 gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointertwgl.setBuffersAndAttributes(gl, obj.programInfo, obj.bufferInfo);//调用 gl.bindTexture, gl.activeTexture, gl.uniformXXXtwgl.setUniforms(obj.programInfo, uni);//调用 gl.drawArrays 或 gl.drawElementstwgl.drawBufferInfo(gl, obj.bufferInfo);});}if (true) {//动画var renderContinuously = 函数(时间){渲染(时间);requestAnimationFrame(renderContinuously);}requestAnimationFrame(renderContinuously);} 别的 {var requestId;var renderRequest = 函数(时间){渲染(时间);requestId = 未定义;}//如果动画var queueRender = function() {如果(!requestId){requestId = requestAnimationFrame(renderRequest);}}window.addEventListener('resize', queueRender);window.addEventListener('scroll', queueRender);队列渲染();}

* {box-sizing: 边框框;-moz-box-sizing: 边框框;}身体 {字体系列:等宽;边距:0;}#C {位置:绝对;顶部:0;宽度:100vw;高度:100vh;}#外{宽度:100%;z-索引:2;位置:绝对;顶部:0px;}#内容 {保证金:自动;填充:2em;}#b {宽度:100%;文本对齐:居中;}.项目清单 {边框:1px纯黑色;边距:2em;填充:1em;宽度:200px;显示:内联块;}.list-item .view {宽度:100px;高度:100px;向左飘浮;边距:0 1em 1em 0;}.list-item .description {填充左:2em;}@media only screen and (max-width : 500px) {#内容 {宽度:100%;}.项目清单 {边距:0.5em;}.list-item .description {填充左:0em;}}

<script src="//twgljs.org/dist/4.x/twgl-full.min.js"><;/脚本><身体><canvas id="c"></canvas><div id="外层"><div id="内容"><div id="b">项目列表</div><div id="列表"></div>

<script id="list-item-template" type="notjs"><div class="view"></div><div class="description">Lorem ipsum dolor sat amet, conse ctetur adipi scing elit.

<script id="vs" type="notjs">统一 mat4 u_worldViewProjection;统一 vec3 u_lightWorldPos;统一 mat4 u_world;统一 mat4 u_viewInverse;统一 mat4 u_worldInverseTranspose;属性 vec4 a_position;属性 vec3 a_normal;属性 vec2 a_texcoord;改变 vec4 v_position;不同的 vec2 v_texCoord;不同的 vec3 v_normal;不同的 vec3 v_surfaceToLight;不同的 vec3 v_surfaceToView;无效主(){v_texCoord = a_texcoord;v_position = (u_worldViewProjection * a_position);v_normal = (u_worldInverseTranspose * vec4(a_normal, 0)).xyz;v_surfaceToLight = u_lightWorldPos - (u_world * a_position).xyz;v_surfaceToView = (u_viewInverse[3] - (u_world * a_position)).xyz;gl_Position = v_position;}<script id="fs" type="notjs">精密中等浮点数;改变 vec4 v_position;不同的 vec2 v_texCoord;不同的 vec3 v_normal;不同的 vec3 v_surfaceToLight;不同的 vec3 v_surfaceToView;统一 vec4 u_lightColor;统一 vec4 u_diffuseMult;统一的 sampler2D u_diffuse;统一 vec4 u_specular;均匀浮动 u_shininess;统一浮动 u_specularFactor;vec4 点燃(浮动 l,浮动 h,浮动 m){返回 vec4(1.0,abs(l),//max(l, 0.0),(l>0.0) ?pow(max(0.0, h), m) : 0.0,1.0);}无效主(){vec4diffuseColor = texture2D(u_diffuse, v_texCoord) * u_diffuseMult;vec3 a_normal = normalize(v_normal);vec3 surfaceToLight = normalize(v_surfaceToLight);vec3 surfaceToView = normalize(v_surfaceToView);vec3 halfVector = normalize(surfaceToLight +surfaceToView);vec4 litR = lit(dot(a_normal,surfaceToLight),点(a_normal,halfVector),u_shininess);vec4 outColor = vec4((u_lightColor * (diffuseColor * litR.y +u_specular * litR.z * u_specularFactor)).rgb,漫射颜色.a);gl_FragColor = outColor;}</script>

如果您有手机,您可以在此处全屏看到类似的手机.

My research lab is working on a webpage that displays a long scrollable list of 3d models, about 50 or so. Our first idea was to do this with separate THREE.js WebGL contexts, but it seems this isn't advisable given the architecture of WebGL, and browsers seem to limit the number of contexts on a page to about 2^4.

I don't need these contexts to do anything very impressive: the individual geometries only have a few hundred triangles, with no textures, and only one at a time ever animates when using the mouse to rotate its camera.

Can I persuade WebGL to do what I want in a way that the browser won't complain about? I thought perhaps of having a single big geometry with all my individual models lined up next to each other, and separate canvases with viewports showing just one model each. But it seems that isn't supported. (Multiple views are allowed in the same context, but that's not very useful for me.)

Thanks for any ideas!

解决方案

It's not clear why you think you need multiple webgl contexts. I'm guessing because you want a list like this

1. [img] description
         description

2. [img] description
         description

3. [img] description
         description

Or something?

Some ideas

  1. make one canvas big enough for the screen, set its CSS so it doesn't scroll with the rest of the page. Draw the models aligned with whatever other HTML you want that does scroll.

  2. make an offscreen webgl canvas and use canvas2d elements to display.

    For each model render the model and then call

    someCanvas2DContextForElementN.drawImage(webGLcanvasElement, ...);
    

Given there are probably only ever a few canvases visible you only need to update those ones. In fact it's probably a good idea to recycle them. In other words, rather than make 12000 canvaes or a 12000 element list make just enough to fit on the screen and update them as you scroll.

Personally I'd probably pick #1 if my page design allowed it. Seems to work, see below.


It turned out to be really easy. I just took this sample that was drawing 100 objects and made it draw one object at a time.

After clearing the screen turn on the scissor test

gl.enable(gl.SCISSOR_TEST);

Then, for each object

// get the element that is a place holder for where we want to
// draw the object
var viewElement = obj.viewElement;

// get its position relative to the page's viewport
var rect = viewElement.getBoundingClientRect();

// check if it's offscreen. If so skip it
if (rect.bottom < 0 || rect.top  > gl.canvas.clientHeight ||
    rect.right  < 0 || rect.left > gl.canvas.clientWidth) {
  return;  // it's off screen
}

// set the viewport
var width  = rect.right - rect.left;
var height = rect.bottom - rect.top;
var left   = rect.left;
var bottom = gl.canvas.clientHeight - rect.bottom - 1;

gl.viewport(left, bottom, width, height);
gl.scissor(left, bottom, width, height);

I'm not 100% sure if I need to add 1 the width and height or not. I suppose I should look that up.

In any case I compute a new projection matrix for every rendered object just to make the code generic. The placeholder divs could be different sizes.

Update:

the solution originally posted here used position: fixed on the canvas to keep it from scrolling. The new solution uses position: absolute and updates the transform just before rendering like this

  gl.canvas.style.transform = `translateY(${window.scrollY}px)`;

With the previous solution the shapes getting re-drawn in their matching positions could lag behind the scrolling. With the new solution the canvas scrolls until we get time to update it. That means shapes might be missing for a few frames if we can't draw quick enough but it looks much better than the scrolling not matching.

The sample below is the updated solution.

"use strict";
// using twgl.js because I'm lazy
    twgl.setAttributePrefix("a_");
    var m4 = twgl.m4;
    var gl = twgl.getWebGLContext(document.getElementById("c"));
    // compiles shaders, links program, looks up locations
    var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);

    // calls gl.creatBuffer, gl.bindBuffer, gl.bufferData for each shape
    // for positions, normals, texcoords
    var shapes = [
      twgl.primitives.createCubeBufferInfo(gl, 2),
      twgl.primitives.createSphereBufferInfo(gl, 1, 24, 12),
      twgl.primitives.createPlaneBufferInfo(gl, 2, 2),
      twgl.primitives.createTruncatedConeBufferInfo(gl, 1, 0, 2, 24, 1),
      twgl.primitives.createCresentBufferInfo(gl, 1, 1, 0.5, 0.1, 24),
      twgl.primitives.createCylinderBufferInfo(gl, 1, 2, 24, 2),
      twgl.primitives.createDiscBufferInfo(gl, 1, 24),
      twgl.primitives.createTorusBufferInfo(gl, 1, 0.4, 24, 12),
    ];

    function rand(min, max) {
      return min + Math.random() * (max - min);
    }

    // Shared values
    var lightWorldPosition = [1, 8, -10];
    var lightColor = [1, 1, 1, 1];
    var camera = m4.identity();
    var view = m4.identity();
    var viewProjection = m4.identity();

    var tex = twgl.createTexture(gl, {
      min: gl.NEAREST,
      mag: gl.NEAREST,
      src: [
        255, 255, 255, 255,
        192, 192, 192, 255,
        192, 192, 192, 255,
        255, 255, 255, 255,
      ],
    });
        
    var randColor = function() {
        var color = [Math.random(), Math.random(), Math.random(), 1];
        color[Math.random() * 3 | 0] = 1; // make at least 1 bright
        return color;
    };

    var objects = [];
    var numObjects = 100;
    var list = document.getElementById("list");
    var listItemTemplate = document.getElementById("list-item-template").text;
    for (var ii = 0; ii < numObjects; ++ii) {
      var listElement = document.createElement("div");
      listElement.innerHTML = listItemTemplate;
      listElement.className = "list-item";
      var viewElement = listElement.querySelector(".view");
      var uniforms = {
        u_lightWorldPos: lightWorldPosition,
        u_lightColor: lightColor,
        u_diffuseMult: randColor(),
        u_specular: [1, 1, 1, 1],
        u_shininess: 50,
        u_specularFactor: 1,
        u_diffuse: tex,
        u_viewInverse: camera,
        u_world: m4.identity(),
        u_worldInverseTranspose: m4.identity(),
        u_worldViewProjection: m4.identity(),
      };
      objects.push({
        ySpeed: rand(0.1, 0.3),
        zSpeed: rand(0.1, 0.3),
        uniforms: uniforms,
        viewElement: viewElement,
        programInfo: programInfo,
        bufferInfo: shapes[ii % shapes.length],
      });
      list.appendChild(listElement);
    }

    var showRenderingArea = false;

    function render(time) {
      time *= 0.001;      
      twgl.resizeCanvasToDisplaySize(gl.canvas);
      
      gl.canvas.style.transform = `translateY(${window.scrollY}px)`;

      gl.enable(gl.DEPTH_TEST);
      gl.disable(gl.SCISSOR_TEST);
      gl.clearColor(0, 0, 0, 0);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      gl.enable(gl.SCISSOR_TEST);

      if (showRenderingArea) {
        gl.clearColor(0, 0, 1, 1);
      }

      var eye = [0, 0, -8];
      var target = [0, 0, 0];
      var up = [0, 1, 0];

      m4.lookAt(eye, target, up, camera);
      m4.inverse(camera, view);

      objects.forEach(function(obj, ndx) {
        var viewElement = obj.viewElement;
        // get viewElement's position
        var rect = viewElement.getBoundingClientRect();
        if (rect.bottom < 0 || rect.top  > gl.canvas.clientHeight ||
            rect.right  < 0 || rect.left > gl.canvas.clientWidth) {
          return;  // it's off screen
        }

        var width  = rect.right - rect.left;
        var height = rect.bottom - rect.top;
        var left   = rect.left;
        var bottom = gl.canvas.clientHeight - rect.bottom - 1;

        gl.viewport(left, bottom, width, height);
        gl.scissor(left, bottom, width, height);

        if (showRenderingArea) {
          gl.clear(gl.COLOR_BUFFER_BIT);
        }

        var projection = m4.perspective(30 * Math.PI / 180, width / height, 0.5, 100);
        m4.multiply(projection, view, viewProjection);

        var uni = obj.uniforms;
        var world = uni.u_world;
        m4.identity(world);
        m4.rotateY(world, time * obj.ySpeed, world);
        m4.rotateZ(world, time * obj.zSpeed, world);
        m4.transpose(m4.inverse(world, uni.u_worldInverseTranspose), uni.u_worldInverseTranspose);
        m4.multiply(viewProjection, uni.u_world, uni.u_worldViewProjection);

        gl.useProgram(obj.programInfo.program);
        // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
        twgl.setBuffersAndAttributes(gl, obj.programInfo, obj.bufferInfo);
        // calls gl.bindTexture, gl.activeTexture, gl.uniformXXX
        twgl.setUniforms(obj.programInfo, uni);
        // calls gl.drawArrays or gl.drawElements
        twgl.drawBufferInfo(gl, obj.bufferInfo);
      });
    }

    if (true) { // animated
        var renderContinuously = function(time) {
            render(time);
            requestAnimationFrame(renderContinuously);
        }
        requestAnimationFrame(renderContinuously);
    } else {
        var requestId;
        var renderRequest = function(time) {
            render(time);
            requestId = undefined;
        }
        // If animated
        var queueRender = function() {
            if (!requestId) {
              requestId = requestAnimationFrame(renderRequest);
            }
        }

        window.addEventListener('resize', queueRender);
        window.addEventListener('scroll', queueRender);

        queueRender();
    }

* {
          box-sizing: border-box;
          -moz-box-sizing: border-box;
      }
      body {
        font-family: monospace;
        margin: 0;
      }
      #c {
          position: absolute;
          top: 0;
          width: 100vw;
          height: 100vh;
      }
      #outer {
          width: 100%;
          z-index: 2;
          position: absolute;
          top: 0px;
      }
      #content {
          margin: auto;
          padding: 2em;
      }
      #b {
        width: 100%;
        text-align: center;
      }
      .list-item {
          border: 1px solid black;
          margin: 2em;
          padding: 1em;
          width: 200px;
          display: inline-block;
      }
      .list-item .view {
          width: 100px;
          height: 100px;
          float: left;
          margin: 0 1em 1em 0;
      }
      .list-item .description {
          padding-left: 2em;
      }

      @media only screen and (max-width : 500px) {
          #content {
              width: 100%;
          }
          .list-item {
              margin: 0.5em;
          }
          .list-item .description {
              padding-left: 0em;
          }
      }

<script src="//twgljs.org/dist/4.x/twgl-full.min.js"></script>
  <body>
    <canvas id="c"></canvas>
    <div id="outer">
      <div id="content">
        <div id="b">item list</div>
        <div id="list"></div>
      </div>
    </div>
  </body>
  <script id="list-item-template" type="notjs">
    <div class="view"></div>
    <div class="description">Lorem ipsum dolor sit amet, conse ctetur adipi scing elit. </div>
  </script>
  <script id="vs" type="notjs">
uniform mat4 u_worldViewProjection;
uniform vec3 u_lightWorldPos;
uniform mat4 u_world;
uniform mat4 u_viewInverse;
uniform mat4 u_worldInverseTranspose;

attribute vec4 a_position;
attribute vec3 a_normal;
attribute vec2 a_texcoord;

varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;

void main() {
  v_texCoord = a_texcoord;
  v_position = (u_worldViewProjection * a_position);
  v_normal = (u_worldInverseTranspose * vec4(a_normal, 0)).xyz;
  v_surfaceToLight = u_lightWorldPos - (u_world * a_position).xyz;
  v_surfaceToView = (u_viewInverse[3] - (u_world * a_position)).xyz;
  gl_Position = v_position;
}
  </script>
  <script id="fs" type="notjs">
precision mediump float;

varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;

uniform vec4 u_lightColor;
uniform vec4 u_diffuseMult;
uniform sampler2D u_diffuse;
uniform vec4 u_specular;
uniform float u_shininess;
uniform float u_specularFactor;

vec4 lit(float l ,float h, float m) {
  return vec4(1.0,
              abs(l),//max(l, 0.0),
              (l > 0.0) ? pow(max(0.0, h), m) : 0.0,
              1.0);
}

void main() {
  vec4 diffuseColor = texture2D(u_diffuse, v_texCoord) * u_diffuseMult;
  vec3 a_normal = normalize(v_normal);
  vec3 surfaceToLight = normalize(v_surfaceToLight);
  vec3 surfaceToView = normalize(v_surfaceToView);
  vec3 halfVector = normalize(surfaceToLight + surfaceToView);
  vec4 litR = lit(dot(a_normal, surfaceToLight),
                    dot(a_normal, halfVector), u_shininess);
  vec4 outColor = vec4((
  u_lightColor * (diffuseColor * litR.y +
                u_specular * litR.z * u_specularFactor)).rgb,
      diffuseColor.a);
  gl_FragColor = outColor;
}
  </script>

If you have a phone you can see a similar one fullscreen here.

这篇关于同一页面上的多个 WebGL 模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆