1.全景图
- 1)顶点着色器
attribute vec2 aPosition;
varying vec4 vPosition;
void main() {
gl_Position = vec4(aPosition,1.0,1.0);
vPosition = vec4(aPosition,0.0,1.0);
}
- 2)片元着色器
precision mediump float;
uniform samplerCube uImage;
uniform mat4 uMatrix;
varying vec4 vPosition;
void main() {
vec4 t = uMatrix* vPosition;
gl_FragColor = textureCube(uImage, normalize(t.xyz / t.w));
}
- 3)初始化立方体贴图
//加载立方体贴图
function initCubeTex(gl, texture, target, url) {
return new Promise((resolve) => {
var image = new Image();
image.src = url; //必须同域
image.onload = () => {
console.log(target, url);
gl.texImage2D(target, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
resolve({
image, texture });
};
});
}
async function initCubeTexture(gl, images) {
const faceMap = {
//前
f: gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
//后
b: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
//上
u: gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
//下
d: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
//左
l: gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
//右
r: gl.TEXTURE_CUBE_MAP_POSITIVE_X
};
//创建一个立方体贴图TEXTURE_CUBE_MAP
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
//给不同立方体不同面加上图片
for (let k in images) {
await initCubeTex(gl, texture, faceMap[k], images[k]);
}
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
}
const images = {
//前
f: 'images/cube/f.jpg',
//后
b: 'images/cube/b.jpg',
//上
u: 'images/cube/u.jpg',
//下
d: 'images/cube/d.jpg',
//左
l: 'images/cube/l.jpg',
//右
r: 'images/cube/r.jpg'
};
await initCubeTexture(gl, images);
gl.activeTexture(gl.TEXTURE0);
gl.uniform1i(gl.getUniformLocation(gl.program, 'uImage'), 0);
- 4)参数赋值
//相机 用于观看全景图
var cameraMatrix = mat4.create();
mat4.lookAt(cameraMatrix, [settings.x, settings.y, settings.z], [0, 0, 0], [0, 1, 0]);
var viewMatrix = mat4.create();
mat4.invert(viewMatrix, cameraMatrix);
// 清除移动的部分
viewMatrix[12] = 0;
viewMatrix[13] = 0;
viewMatrix[14] = 0;
var uMatrix = mat4.create();
mat4.invert(uMatrix, mat4.multiply(uMatrix, projectionMatrix, viewMatrix));
gl.uniformMatrix4fv(gl.getUniformLocation(program, 'uMatrix'), false, uMatrix);
注意:samplerCube立方体贴图与sampler2D普通图片贴图是不一样的。
2.镜面立方体(环境贴图立方体)
- 1)顶点着色器
attribute vec3 aPosition;//物体点
attribute vec3 aNormal;//法线点
uniform mat4 uProjectionMatrix;//透视投影
uniform mat4 uView;//视图矩阵
uniform mat4 uModelViewMatrix;//模型变换
varying vec3 vWorldPosition;//世界物体点
varying vec3 vWorldNormal;//世界物体法线
void main() {
vec4 pos=vec4(aPosition,1.0);
gl_Position = uProjectionMatrix * uView * uModelViewMatrix * pos;
vWorldPosition = (uModelViewMatrix * pos).xyz;
vWorldNormal = mat3(uModelViewMatrix) * aNormal;
}
- 2)片元着色器
放射方向 = 眼睛看向物体的方向 – 2 ∗ dot(物体表面法线, 眼睛看向物体的方向) ∗ 物体表面法线
precision highp float;
varying vec3 vWorldPosition;
varying vec3 vWorldNormal;
uniform samplerCube uImage;//立方体贴图
uniform vec3 uCameraPosition;//相机位置
void main() {
vec3 worldNormal = normalize(vWorldNormal);
vec3 eyeToSurfaceDir = normalize(vWorldPosition - uCameraPosition);
//反射
vec3 direction = reflect(eyeToSurfaceDir,worldNormal);
gl_FragColor = textureCube(uImage, direction);
}
- 3)立方体环境贴图与全景图立方体贴图代码一致
- 4)参数赋值
var cameraPosition = [0, 0, 2];
var cameraMatrix = mat4.create();
mat4.lookAt(cameraMatrix, [0, 0, 2], [0, 0, 0], [0, 1, 0]);
initModelViewMatrix(gl, settings);
//从相机矩阵创建视图
var viewMatrix = mat4.create();
mat4.invert(cameraMatrix, cameraMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(program, 'uView'), false, viewMatrix);
gl.uniform3fv(gl.getUniformLocation(program, 'uCameraPosition'), cameraPosition);
3.全景图加上镜面立方体
function drawCube() {
//切换成立方体程序
gl.program = cubeProgram;
gl.useProgram(cubeProgram);
//如果新的深度值小于存储的值,则将通过模具测试
gl.depthFunc(gl.LESS);
//……
}
function drawEnv() {
//切换成全景图程序
gl.program = envProgram;
gl.useProgram(envProgram);
//如果新的深度值小于或等于存储的值,则将通过模具测试。
gl.depthFunc(gl.LEQUAL);
//……
}
function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//相机位置
mat4.lookAt(cameraMatrix, [settings.cx, settings.cy, settings.cz], [0, 0, 0], [0, 1, 0]);
//从相机矩阵创建视图
mat4.invert(viewMatrix, cameraMatrix);
//清除移动
viewMatrix[12] = 0;
viewMatrix[13] = 0;
viewMatrix[14] = 0;
drawCube();
drawEnv();
requestAnimationFrame(drawScene);
}
drawScene();
注意:画立方体和全景图的深度函数用的是不同模式设置
先初始化立方体和全景图的着色器程序,然后初始化贴图和一些共用的变量值,画场景的时候,buffer等值每次画都得赋值一遍,否则会被清掉,物体不完整
- 将反射的方向设置成这样,就会出现透【明放大镜】的效果
vec3 direction = eyeToSurfaceDir;
- 将反射的方向设置成这样,就会出现【近视眼透明立方体】的效果
vec3 direction =eyeToSurfaceDir*worldNormal;
github地址
https://github.com/xiaolidan00/my-webgl
参考
https://webglfundamentals.org/
- 《WebGL编程指南》
- 《webGL 3D开发实战详解 第2版》