【问题标题】:WebGL canvas backgroundWebGL 画布背景
【发布时间】:2020-07-15 13:30:29
【问题描述】:

我想在我的场景中简单地添加一个“天空”背景,这样它就不会覆盖对象,而只是在它们之后可见。

我试过了,但如果不将它应用到场景中绘制的其他对象上,我不知道要这样做。

不幸的是,我必须在不使用任何高级库(例如 three.js 等)的情况下完成所有这些操作。

我想使用的背景在 /Assets/sky.jpg

这里是js文件:

var program0;
var program1;
var gl;
var shaderDir;
var baseDir;
var lastUpdateTime;

var boatModel;
var rockModel;
var rock2Model;
var oceanModel;





var object = [];

//attributes and uniforms
var positionAttributeLocation = Array();
var uvAttributeLocation = Array();
var matrixLocation = Array();
var textLocation = Array();
var normalAttributeLocation = Array();
var normalMatrixPositionHandle = Array();
var worldViewMatrixLocation = Array();
var worldViewMatrixLocation_transpose = Array();

var materialDiffColorHandle = Array();
var lightDirectionHandle = Array();
var lightColorHandle = Array();
var ambientLightcolorHandle = Array();
var specularColorHandle = Array();
var specShineHandle = Array();


var vaos = new Array();
var textures = new Array();
var modelStr = Array();
var modelTexture = Array();

//matrices
var viewMatrix;
var perspectiveMatrix;

//lights
//define directional light
var dirLightAlpha = -utils.degToRad(180);
var dirLightBeta = -utils.degToRad(100);
var directionalLight;
var directionalLightColor;
var ambientLight = [0.5, 0.5, 0.5];
var specularColor = [0.0, 0.0, 0.0];
var specShine = 0.0;

//camera
var cx = 0;
var cy = 0;
var cz = 1;
var camAngle = 0;
var camElev = 5;

//boat kinematics
var linearDir = 0;
var linearVel = 0;
var velX = 0;
var velZ = 0;
var maxLinearVel = 0.01;
var linearAcc = 0.0001;
var linearDrag = 0.005;

var turningDir = 0;
var angularVel = 0.0;
var maxAngularVel = 0.2;
var angularAcc = 0.01;
var angularDrag = 0.01;

modelStr[0] = 'Assets/Boat/Boat.obj';
modelStr[1] = 'Assets/Rocks/Rock1/rock1.obj';
modelStr[2] = 'Assets/Rocks/Rock2/Rock_1.obj';
modelStr[3] = 'Assets/ocean-obj/ocean.obj';
//modelStr[3] = 'Assets/ocean2/hdri-ca-sky.obj';

modelTexture[0] = 'Assets/Boat/textures/boat_diffuse.bmp';
modelTexture[1] = 'Assets/Rocks/Rock1/textures/rock_low_Base_Color.png';
modelTexture[2] = 'Assets/Rocks/Rock2/Rock_1_Tex/Rock_1_Base_Color.jpg';
modelTexture[3] = 'Assets/ocean-obj/woter.jpg';
//modelTexture[3] = 'Assets/ocean2/CA-Sky-2016-04-15-11-30-am.jpg';

modelTexture[4] = 'Assets/Sea/sea.jpg'

var nFrame = 0;


/***********************************************************************************************/

class Item {
  x; y; z;
  Rx; Ry; Rz;
  S;

  vertices;
  normals;
  indices;
  texCoords;

  materialColor;


  constructor(x, y, z, Rx, Ry, Rz, S) {

    this.x = x;
    this.y = y;
    this.z = z;
    this.Rx = Rx;
    this.Ry = Ry;
    this.Rz = Rz;
    this.S = S;



  }

  buildWorldMatrix() {
    return utils.MakeWorld(this.x, this.y, this.z, this.Rx, this.Ry, this.Rz, this.S);

  }


  setAttr(objectVertices, objectNormals, objectIndices, objectTexCoords) {
    this.vertices = objectVertices;
    this.normals = objectNormals;
    this.indices = objectIndices;
    this.texCoords = objectTexCoords;

  }

  setMaterialColor(materialColorArray) {
    this.materialColor = materialColorArray;

  }


}

//objects
var rock = new Item(1.0, -0.5, -3.0, 0.0, 0.0, 0.0, 1.0 / 20.0);
var boat = new Item(0.0, -0.15, 0.0, 90.0, 0.0, 0.0, 1.0 / 1000.0);
var rock2 = new Item(-1.0, -0.4, -3, -30.0, 0.0, 0.0, 1.0 / 10.0);
var ocean = new Item(0.0, -0.02, 0.0, 90.0, 0.0, 0.0, 100.0);

function isPowerOf2(value) {
  return (value & (value - 1)) == 0;
}

function main() {

  utils.resizeCanvasToDisplaySize(gl.canvas);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

  setFloorCoord();
  /* Load corresponding information from the models */
  object[0] = boat;
  object[1] = rock;
  object[2] = rock2;
  object[3] = ocean;

  boat.setAttr(boatModel.vertices, boatModel.vertexNormals, boatModel.indices, boatModel.textures);
  boat.setMaterialColor([1.0, 1.0, 1.0]); // set material color for boat

  rock.setAttr(rockModel.vertices, rockModel.vertexNormals, rockModel.indices, rockModel.textures);
  rock.setMaterialColor([1.0, 1.0, 1.0]); // set material color for rock

  rock2.setAttr(rock2Model.vertices, rock2Model.vertexNormals, rock2Model.indices, rock2Model.textures);
  rock2.setMaterialColor([1.0, 1.0, 1.0]);

  ocean.setAttr(oceanModel.vertices, oceanModel.vertexNormals, oceanModel.indices, oceanModel.textures);
  ocean.setMaterialColor([1.0, 1.0, 1.0]);



  directionalLight = [Math.cos(dirLightAlpha) * Math.cos(dirLightBeta),
  Math.sin(dirLightAlpha),
  Math.cos(dirLightAlpha) * Math.sin(dirLightBeta)
  ];
  directionalLightColor = [1.0, 1.0, 1.0];

  /* Retrieve the position of the attributes and uniforms */
  getShadersPos()

  objectWorldMatrix = Array();

  objectWorldMatrix[0] = boat.buildWorldMatrix(); //boat WorldMatrix
  objectWorldMatrix[1] = rock.buildWorldMatrix(); //rock WorlMatrix
  objectWorldMatrix[2] = rock2.buildWorldMatrix();
  objectWorldMatrix[3] = ocean.buildWorldMatrix();


  perspectiveMatrix = utils.MakePerspective(90, gl.canvas.width / gl.canvas.height, 0.1, 100.0);
  viewMatrix = utils.MakeView(0.0, 1.0, 1.0, 15.0, 0.0);

  setBuffers();
  drawScene();

}

async function init() {

  var path = window.location.pathname;
  var page = path.split("/").pop();
  baseDir = window.location.href.replace(page, '');
  shaderDir = baseDir + "shaders/";

  var canvas = document.getElementById("c");


  lastUpdateTime = (new Date).getTime();

  gl = canvas.getContext("webgl2");
  if (!gl) {
    document.write("GL context not opened");
    return;
  }

  

  await utils.loadFiles([shaderDir + 'vs.glsl', shaderDir + 'fs.glsl'], function (shaderText) {
    var vertexShader = utils.createShader(gl, gl.VERTEX_SHADER, shaderText[0]);
    var fragmentShader = utils.createShader(gl, gl.FRAGMENT_SHADER, shaderText[1]);
    program0 = utils.createProgram(gl, vertexShader, fragmentShader);

  });

  await utils.loadFiles([shaderDir + 'vs_unlit.glsl', shaderDir + 'fs_unlit.glsl'], function (shaderText) {
    var vertexShader = utils.createShader(gl, gl.VERTEX_SHADER, shaderText[0]);
    var fragmentShader = utils.createShader(gl, gl.FRAGMENT_SHADER, shaderText[1]);

    program1 = utils.createProgram(gl, vertexShader, fragmentShader);
  });



  //###################################################################################
  //This loads the obj model in the boatModel variable
  var boatObjStr = await utils.get_objstr(baseDir + modelStr[0]);
  boatModel = new OBJ.Mesh(boatObjStr);
  //###################################################################################

  //###################################################################################
  //This loads the obj model in the rockModel variable
  var rockObjStr = await utils.get_objstr(baseDir + modelStr[1]);
  rockModel = new OBJ.Mesh(rockObjStr);
  //###################################################################################

  //###################################################################################
  //This loads the obj model in the rockModel variable
  var rock2ObjStr = await utils.get_objstr(baseDir + modelStr[2]);
  rock2Model = new OBJ.Mesh(rock2ObjStr);
  //###################################################################################

  var oceanObjStr = await utils.get_objstr(baseDir + modelStr[3]);
  oceanModel = new OBJ.Mesh(oceanObjStr);

  initControls(canvas);

  main();
}



function getShadersPos() {
  positionAttributeLocation[0] = gl.getAttribLocation(program0, "a_position");
  uvAttributeLocation[0] = gl.getAttribLocation(program0, "a_uv");
  matrixLocation[0] = gl.getUniformLocation(program0, "matrix");
  worldViewMatrixLocation[0] = gl.getUniformLocation(program0, "worldviewmatrix");
  worldViewMatrixLocation_transpose[0] = gl.getUniformLocation(program0, "worldviewmatrix_t");
  textLocation[0] = gl.getUniformLocation(program0, "u_texture");
  normalAttributeLocation[0] = gl.getAttribLocation(program0, "inNormal");
  normalMatrixPositionHandle[0] = gl.getUniformLocation(program0, 'nMatrix');

  materialDiffColorHandle[0] = gl.getUniformLocation(program0, 'mDiffColor');
  lightDirectionHandle[0] = gl.getUniformLocation(program0, 'lightDirection');
  lightColorHandle[0] = gl.getUniformLocation(program0, 'lightColor');
  ambientLightcolorHandle[0] = gl.getUniformLocation(program0, 'ambientLightcolor');
  specularColorHandle[0] = gl.getUniformLocation(program0, 'specularColor');
  specShineHandle[0] = gl.getUniformLocation(program0, 'SpecShine');

  
}

function setBuffers() {

  for (let i = 0; i < object.length; i++) {

    vaos[i] = gl.createVertexArray();
    gl.bindVertexArray(vaos[i])

    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(object[i].vertices), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(positionAttributeLocation[0]);
    gl.vertexAttribPointer(positionAttributeLocation[0], 3, gl.FLOAT, false, 0, 0);

    var uvBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(object[i].texCoords), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(uvAttributeLocation[0]);
    gl.vertexAttribPointer(uvAttributeLocation[0], 2, gl.FLOAT, false, 0, 0);

    var indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(object[i].indices), gl.STATIC_DRAW);

    var normalBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(object[i].normals), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(normalAttributeLocation[0]);
    gl.vertexAttribPointer(normalAttributeLocation[0], 3, gl.FLOAT, false, 0, 0);

    textures[i] = gl.createTexture();

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, textures[i]);

    image = new Image();
    image.crossOrigin = "anonymous";
    image.src = baseDir + modelTexture[i];

    image.onload = function (texture, image) {
      return function () {
        gl.activeTexture(gl.TEXTURE0)
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
        // Check if the image is a power of 2 in both dimensions.
        if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
          // Yes, it's a power of 2. Generate mips.
          gl.generateMipmap(gl.TEXTURE_2D);
        } else {
          // No, it's not a power of 2. Turn off mips and set wrapping to clamp to edge
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        }
      };
    }(textures[i], image);

  }
}



function drawObjects() {
  for (let i = 0; i < object.length; ++i) {
    gl.useProgram(program0);
    var viewWorldMatrix = utils.multiplyMatrices(viewMatrix, objectWorldMatrix[i]);
    var projectionMatrix = utils.multiplyMatrices(perspectiveMatrix, viewWorldMatrix);
    gl.uniformMatrix4fv(matrixLocation[0], gl.FALSE, utils.transposeMatrix(projectionMatrix));
    gl.uniformMatrix4fv(worldViewMatrixLocation_transpose[0], gl.FALSE, utils.transposeMatrix(utils.invertMatrix(utils.transposeMatrix(viewWorldMatrix))));
    gl.uniformMatrix4fv(worldViewMatrixLocation[0], gl.FALSE, utils.transposeMatrix(viewWorldMatrix));
    gl.uniformMatrix4fv(normalMatrixPositionHandle[0], gl.FALSE, utils.transposeMatrix(utils.invertMatrix(utils.transposeMatrix(objectWorldMatrix[i]))));

    gl.uniform3fv(materialDiffColorHandle[0], object[i].materialColor);
    gl.uniform3fv(lightColorHandle[0], directionalLightColor);
    gl.uniform3fv(lightDirectionHandle[0], directionalLight);
    gl.uniform3fv(ambientLightcolorHandle[0], ambientLight);
    gl.uniform3fv(specularColorHandle[0], specularColor);
    gl.uniform1f(specShineHandle[0], specShine);


    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, textures[i]);
    gl.uniform1i(textLocation[0], textures[i]);


    gl.bindVertexArray(vaos[i]);
    gl.drawElements(gl.TRIANGLES, object[i].indices.length, gl.UNSIGNED_SHORT, 0);

  }

  
}

var counter = 0;

function animate(item) {
  var currentTime = (new Date).getTime();
  if (lastUpdateTime != null) {
    boatDynamic(currentTime);
    var deltaC = (30 * (currentTime - lastUpdateTime)) / 1000.0;
    //item.z += deltaC/100;


    //item.Rz += deltaC;
  }

  /* depending on which object we want to animate we change the worldmatrix of the object */
  //objectWorldMatrix[0] = utils.MakeWorld(0.0, item.y, item.z, item.Rx, item.Ry, item.Rz, item.S);
  counter += 0.005;
  //item.z = counter % 2;
  //item.y = counter;

  //(0, -1, 2, 45, 0)
  //item.z -= 0.002;
  viewMatrix = utils.MakeView(cx + item.x, cy + 1, 2 + item.z, camElev, 0);

  //<---- la barca si muove verso la z negativa
  //item.y += 0.002;

  objectWorldMatrix[0] = item.buildWorldMatrix();


  //objectWorldMatrix[1] = rock.buildWorldMatrix();
  //objectWorldMatrix[2] = rock2.buildWorldMatrix();
  lastUpdateTime = currentTime;
}

function drawScene() {

  animate(boat);

  gl.clearColor(0.85, 0.85, 0.85, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.enable(gl.DEPTH_TEST);
  

  // DRAW THE OBJECTS IN THE SCENE
  drawObjects();

  window.requestAnimationFrame(drawScene);
}

//controls
var keys = [];
var vz = 0.0;
var rvy = 0.0;

var keyFunctionDown = function (e) {
  if (!keys[e.keyCode]) {
    keys[e.keyCode] = true;
    switch (e.keyCode) {
      case 37: //LEFT ARROW KEY DOWN
        turningDir = - 1;
        break;

      case 39: //RIGHT ARROW KEY DOWN
        turningDir = + 1;
        break;

      case 38: //UP ARROW KEY DOWN
        linearDir = + 1;
        break;

      case 40: //DOWN ARROW KEY DOWN
        linearDir = - 1;

        break;

      //camera controls
      case 87:
        camElev += 5;
        console.log(camElev)
        break;
      case 83:
        camElev -= 5;
        console.log(camElev)
        break;
    }
  }
}

var keyFunctionUp = function (e) {
  if (keys[e.keyCode]) {
    keys[e.keyCode] = false;
    switch (e.keyCode) {
      case 37: //LEFT ARROW KEY UP
        turningDir = 0;
        break;
      case 39: //RIGHT ARROW KEY UP
        turningDir = 0;
        break;
      case 38: //UP ARROW KEY UP
        linearDir = 0;
        break;
      case 40: //DOWN ARROW KEY DOWN
        linearDir = 0;
        break;
    }
  }
}

function initControls(canvas) {
  window.addEventListener("keyup", keyFunctionUp, false);
  window.addEventListener("keydown", keyFunctionDown, false);


}


function boatDynamic(currentTime) {
  //console.log(linearVel);
  //boat turning
  angularVel += turningDir * angularAcc;
  if (Math.abs(angularVel) >= maxAngularVel)
    angularVel = Math.sign(angularVel) * maxAngularVel;

  //angular velocity degradation
  angularVel = angularVel * (1 - angularDrag);

  boat.Rx += angularVel;

  //boat speed
  linearVel += linearDir * linearAcc;
  if (Math.abs(linearVel) >= maxLinearVel)
    linearVel = Math.sign(linearVel) * maxLinearVel;

  //linear vel degradation
  linearVel = linearVel * (1 - linearDrag)

  //linear velocity axis decomposition
  velX = - linearVel * Math.cos(utils.degToRad(boat.Rx));
  velZ = - linearVel * Math.sin(utils.degToRad(boat.Rx));



  boat.x += velX;
  boat.z += velZ;


  //simple boat "wobbling" around its y axis, must be implemented better
  if (Math.random() > 0.8) {
    boat.Ry += Math.sin(utils.degToRad(currentTime)) / 8;
  }

}


function dirLightChange(value, type) {
  if (type == 'alpha')
    dirLightAlpha = -utils.degToRad(value);
  else
    dirLightBeta = -utils.degToRad(value);

  directionalLight = [Math.cos(dirLightAlpha) * Math.cos(dirLightBeta),
  Math.sin(dirLightAlpha),
  Math.cos(dirLightAlpha) * Math.sin(dirLightBeta)
  ];
  drawObjects();


}

function onColorChange(value, type) {
  let result = HEX2RGB(value);
  var r = result[0] / 255.0;
  var g = result[1] / 255.0;
  var b = result[2] / 255.0;
  if (type == 'ambient')
    ambientLight = [r, g, b];
  else if (type == 'directional')
    directionalLightColor = [r, g, b];
  else if (type == 'material')
    boat.setMaterialColor([r, g, b]);
  else
    specularColor = [r, g, b];
  drawObjects();
}

function onSpecShineChange(value) {
  specShine = value;
  drawObjects();
}


window.onload = init;

这里是整个项目的回购:repo

【问题讨论】:

  • 您可以绘制最小 z 值的矩形,并在其上设置纹理

标签: javascript canvas graphics shader webgl


【解决方案1】:
  • 方法 1 最简单

    1. 清除深度缓冲区
    2. 关闭深度测试
    3. 用你的天空纹理画一架飞机
    4. 开启深度测试
    5. 绘制您的对象
  • 方法2(效率稍高)

    1. 清除深度缓冲区
    2. 将深度函数设置为LESS
    3. 开启深度测试
    4. 绘制不透明的对象
    5. 将深度函数设置为LEQUAL
    6. 在 Z = 1 处使用天空纹理绘制平面
    7. 绘制透明对象

注意:使用自定义着色器在 Z = 1 处绘制平面是最简单的。

示例:https://stackoverflow.com/a/52508687/128511

许多过时的 3D 引擎都尝试使用绘制其他所有内容的系统。换句话说,他们只实现一件事,一个使用单个投影矩阵和视图矩阵绘制所有对象的循环,因此他们必须计算一个模型矩阵来定位平面,以便它恰好出现在当前的 -Z视图和当前投影。这真是愚蠢的国际海事组织。

过时的 3D 引擎做的另一件事是让每个绘制对象使用不同的投影和视图矩阵。在这种情况下,平面的投影和视图矩阵可以设置为类似(假设平面在 Z = 0 处有顶点

   view matrix = identity
   projection matrix = [
      1, 0, 0, 0
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 1, 1,
   ];

  Will move Z to 1.

然后您可能想要设置第一个 1(宽度)和第二个 1(高度),如 this answer 所示(同上)

如果是我,虽然我会做现代引擎所做的事情,并使用不同的着色器,如上面第一个链接中的着色器,仅用于绘制图像。

注意:您可能还想查看skyboxes

【讨论】:

  • 问题是环境会发生变化,我的物体会在环境中移动,所以我需要一种固定的背景,避免与其他物体一起移动。天空盒能做到这一点吗?如何在不使用更高级库的情况下创建它们?那你建议什么?
  • 上面的答案是关于固定背景的。只有底部的注释建议您可能想要查看天空盒。如果您想要固定的背景,请使用答案中建议的解决方案。如果你想要一个天空盒,请按照链接的文章。有lots of free images herehere's a converter to convert them to cubemaps
猜你喜欢
  • 2017-02-15
  • 1970-01-01
  • 2015-03-18
  • 2014-01-03
  • 2014-03-01
  • 2012-02-05
  • 1970-01-01
  • 2022-06-14
  • 1970-01-01
相关资源
最近更新 更多