threejs:vPositionl两种写法的区别

首先我们创建一个mesh,不做任何旋转、平移和缩放:

let c = [
  0, 0, //顶点1坐标
  60, 0, //顶点2坐标
  60, 80, //顶点3坐标
  40, 120, //顶点4坐标
  -20, 80, //顶点5坐标
  0, 0, //顶点6坐标  和顶点1重合
]

let geometry = new THREE.BufferGeometry(); //声明一个空几何体对象
let posArr = [];
let h = 20; //围墙拉伸高度
for (let i = 0; i < c.length - 2; i += 2) {
  // 三角形1  三个顶点坐标
  posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], 0, c[i + 2], c[i + 3], h);
  // 三角形2  三个顶点坐标
  posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], h, c[i], c[i + 1], h);
}
// 设置几何体attributes属性的位置position属性
geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(posArr), 3);
geometry.computeVertexNormals();
let material = new THREE.MeshLambertMaterial({
  color: 0xff0000, //三角面颜色
  side: THREE.DoubleSide, //两面可见
  transparent:true,
  opacity:0.8
});
let mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
model.add(mesh);

如下图:

现在使用着色器来给这个模型围一圈光带,注意,是要在z轴上来处理。

const vertexShader = `
	varying vec3 vPosition;//表示顶点插值后位置数据,与片元数量相同,一一对应
	void main(){
		vPosition = position;// 顶点位置坐标插值计算
		// 投影矩阵 * 视图矩阵 * 模型矩阵 * 模型顶点坐标
		gl_Position = projectionMatrix*viewMatrix*modelMatrix*vec4( position, 1.0 );
	}
`;

const fragmentShader = `
varying vec3 vPosition;

void main() {
  // 整体设置半透明色,记得Materia要开启透明
  gl_FragColor = vec4(1.0,0.0,0.0,0.5);
  if(vPosition.z > 10.0 && vPosition.z < 11.0){
    gl_FragColor = vec4(1.0,1.0,0.0, 1.0);
  }
}
`;

let material = new THREE.ShaderMaterial({
    //顶点着色器对象vertexShader
    vertexShader: vertexShader,
    // 片元着色器对象fragmentShader
    fragmentShader: fragmentShader,
    transparent:true,
    side:THREE.DoubleSide
});

效果如下:

此时,我们在顶点着色器里使用的是vPosition = position;来计算顶点位置插值。

if(vPosition.z > 10.0 && vPosition.z < 11.0){
    gl_FragColor = vec4(1.0,1.0,0.0, 1.0);
  }

我们在z轴上做了条件判断设置光带。

 现在来对模型进行旋转缩放平移等操作。

mesh.rotateX(-Math.PI/4);
mesh.translateY(20);
mesh.scale.set(0.5,0.5,0.5);

光带依旧稳稳的围在模型上,相对位置没有变化。

现在让我们回到模型刚建立的时候,如图1,是一个立起来的围墙,但我们平时的围墙肯定是平下去的,所以我们需要将模型旋转一下,第一段代码稍作修改,最后加一个旋转操作。

let c = [
  0, 0, //顶点1坐标
  60, 0, //顶点2坐标
  60, 80, //顶点3坐标
  40, 120, //顶点4坐标
  -20, 80, //顶点5坐标
  0, 0, //顶点6坐标  和顶点1重合
]

let geometry = new THREE.BufferGeometry(); //声明一个空几何体对象
let posArr = [];
let h = 20; //围墙拉伸高度
for (let i = 0; i < c.length - 2; i += 2) {
  // 三角形1  三个顶点坐标
  posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], 0, c[i + 2], c[i + 3], h);
  // 三角形2  三个顶点坐标
  posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], h, c[i], c[i + 1], h);
}
// 设置几何体attributes属性的位置position属性
geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(posArr), 3);
geometry.computeVertexNormals();
let material = new THREE.MeshLambertMaterial({
  color: 0xff0000, //三角面颜色
  side: THREE.DoubleSide, //两面可见
  transparent:true,
  opacity:0.8
});
// 为了让围墙平放,需要旋转模型
mesh.rotateX(-Math.PI/2);

现在围墙放平了。

这个时候再来给围墙加光带,这时候我们脑子里想到的肯定是在着色器里操作y轴,这样才能将模型围起来,这样写

 if(vPosition.y > 10.0 && vPosition.y < 11.0){
    gl_FragColor = vec4(1.0,1.0,0.0, 1.0);
  }

const vertexShader = `
	varying vec3 vPosition;//表示顶点插值后位置数据,与片元数量相同,一一对应
	void main(){
		vPosition = position;// 顶点位置坐标插值计算
		// 投影矩阵 * 视图矩阵 * 模型矩阵 * 模型顶点坐标
		gl_Position = projectionMatrix*viewMatrix*modelMatrix*vec4( position, 1.0 );
	}
`;

const fragmentShader = `
varying vec3 vPosition;

void main() {
  // 整体设置半透明色
  gl_FragColor = vec4(1.0,0.0,0.0,0.8);
  if(vPosition.y > 10.0 && vPosition.y < 11.0){
    gl_FragColor = vec4(1.0,1.0,0.0, 1.0);
  }
}

结果光带效果如下,并没有如我们期望的围城一圈,秘密就在于vPosition的取值:

vPosition=postion时,获取的是顶点相对模型的坐标,在模型变化前,基于此时看到模型姿态,编写着色器代码,那么之后模型再发生变化,光带也会随之发生相对变化。

那如果已经改变了模型的姿态,比如上个例子里把模型旋转了90度,基于此时看到的模型姿态,则需要使用模型矩阵变换后插值。

试试一下代码:

const vertexShader = `
	varying vec3 vPosition;//表示顶点插值后位置数据,与片元数量相同,一一对应
	void main(){
		// vPosition = position;// 顶点位置坐标插值计算
        vPosition = vec3(modelMatrix * vec4( position, 1.0 ));
		// 投影矩阵 * 视图矩阵 * 模型矩阵 * 模型顶点坐标
		gl_Position = projectionMatrix*viewMatrix*modelMatrix*vec4( position, 1.0 );
	}
`;

const fragmentShader = `
varying vec3 vPosition;

void main() {
  // 整体设置半透明色
  gl_FragColor = vec4(1.0,0.0,0.0,0.8);
  if(vPosition.y > 10.0 && vPosition.y < 11.0){
    gl_FragColor = vec4(1.0,1.0,0.0, 1.0);
  }
}
`;

光带又回到预期位置了。

此时执行mesh.translateZ(10);会发现模型发生了平移,但是光带的位置没有变化,依旧在y轴10的位置。

因为vPosition = vec3(modelMatrix * vec4( position, 1.0 ));获取的是顶点的相对坐标轴的位置,可以理解为绝对坐标,世界坐标,模型移动后,光带的位置仍然不变。

最后的总结:

想要着色器效果随着模型旋转平移缩放能跟随变化,需要在模型发生变化前,根据此时的模型姿态来编写着色器代码,使用vPosition = position;来获取顶点位置坐标插值。

vPosition = vec3(modelMatrix * vec4( position, 1.0 ));慎用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值