web平台使用threejs实现三维视频融合的思路
案例
方案一
- 其实这种效果跟我们看到的投影仪的效果非常类似,就是把视频当做一片片纹理,投射到物体上,那么我们一开始别搞太复杂,能把一张纹理图投射出去就很不错了
- 那么怎么投射呢,threejs有没有类似unity的投射器(projector)可以用呢。不好意思,我找了半天没看到跟project相关的字眼,如果threejs真的有,麻烦告知一声
- 那最悲催的事情就来了,既然没有就得靠自己了,从写shader开始吧
- 投射说白了其实就是你写一段shader程序,然后把shader扔给要投射的物体,物体在渲染时就根据你写的shader把纹理图映射过去,当然,事情肯定没有这么简单,这里主要是为了让你更加容易理解这个过程
- 好吧,让人闻风丧胆的shader编程终究是避无可避的
- 投射就好像在我们看到的场景中有个投影仪一样,那么我们在场景中就用一个相机来替代这个投影仪了,于是现在场景中有两个相机了,一个是我们在屏幕上看到的画面的相机,我们称他为主相机吧,另一个是就是投影相机了,用来投影纹理图的,这两个相机一般位置角度都是不同的,因此这个渲染就是我们的难点了
- 怎么在主相机画面中看到投影相机的画面的投射,这确实是一个头疼问题,好在问题也不是不可以解决,就是要在顶点着色器里把顶点的世界坐标计算出来,然后通过raying这个关键字扔给片元着色器,这样片元着色器就拿到了要渲染的那个片元的世界坐标
- 这个世界坐标有啥用呢,我们的投影相机老大说他要用这个坐标去判断一下是不是在他的视锥体内,不在他范围内他都没想理你
- 还要用这个世界坐标去计算他在投影相机老大里的齐次裁剪坐标,然后去映射纹理
- 投影相机老大还说了,要实现上面两点,必须得把他的视图矩阵和投影矩阵告诉他,不然他没法计算,于是你就要通过uniform关键字偷偷将它们传给片元着色器
- 一切准备就绪,判断在不在投影相机的视锥体内主要就是靠齐次裁剪坐标,如果xyz三个分量的值都在[-w,w]中,那这个片元对应的点的世界坐标就在投影相机老大的地盘里了,在我(指投影相机)地盘里那自然要负起责任,把他的纹理映射坐标给计算出来了
- 怎么计算,就是先把齐次裁剪坐标进行归一化变成ndc坐标(除以w分量就是了),然后再跟屏幕坐标(在opengl左下角才是原点)进行映射,这个映射是通过一个矩阵完成的
- 到这里基本上投射也就完成了
- 以下是顶点着色器
varying vec2 vUv;
varying vec3 worldPos;
void main()
{
vUv = uv;
worldPos=(modelMatrix*vec4( position, 1.0 )).xyz;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
glPos=gl_Position = projectionMatrix * mvPosition;
}
uniform sampler2D textureD;
uniform mat4 mviewMatrix;
uniform mat4 mprojectionMatrix;
varying vec2 vUv;
varying vec3 worldPos;
vec3 RayPlaneIntersection(vec3 ray1,vec3 ray2,vec3 planePoint,vec3 planeVec)
{
vec3 p;
float t;
t = (dot(planeVec,planePoint) - dot(planeVec ,ray1)) / dot(planeVec,ray2);
p = ray1 + t*ray2;
return p;
}
bool judgeInProjectCamera(vec3 worldPos){
vec4 viewPos=mviewMatrix*vec4(worldPos,1.0);
vec4 p_pos=mprojectionMatrix*viewPos;
p_pos=p_pos/p_pos.w;
if(abs(p_pos.x)>1.0||abs(p_pos.y)>1.0||abs(p_pos.z)>1.0){
return false;
}else{
return true;
}
}
void main(void){
vec4 mviewPos=mviewMatrix*vec4(worldPos,1.0);
vec4 ngl_pos=mprojectionMatrix*mviewPos;
if(!judgeInProjectCamera(worldPos)){
gl_FragColor=vec4(1,1,1,0);
}else{
mat4 texNormalMatrix=mat4