如何在shader中实现相机

发布于 2023-04-21 11:52:15

背景

相机在图形学中是一个非常重要的概念,它用于模拟人眼或摄像机捕获三维场景并将其转换为二维图像,在真实相机里面我们经常会面对很多概念,比如相机位置、相机看向的点、还有相机的上方向。就像下面的相机,你能想到什么?

相机.png

跟着我一起来看看shader里面的相机应用吧

前情回顾

我们之前利用光线追踪给大家距离解释了如何应用shader完成三维场景中球体的绘制内容,我将前面代码做了一定总结和归纳,如下所示:

// Author: ice
// Title: 光线追踪

#ifdef GL_ES
precision mediump float;
#endif
#define MAX_ITERATIO_NNUMBER 255
#define MIN_DISTANCE 0.001
#define START_POSITION 0.
#define END_POSITION 100.
uniform vec2 u_resolution;
uniform float u_time;
mat2 r2d(float a) {
    float c = cos(a), s = sin(a);
    return mat2(
        c, s, // column 1
        -s, c // column 2
    );
}
float sdfPlane(vec3 p){
    return p.y;
}
float sdfSphere(vec3 p,vec3 move, float r){
    return length(p-move)-r;
}
float getDistance(vec3 p, float r){
  
    float plane =sdfPlane(p);
    float sphere =sdfSphere(p,vec3(0.,2.,-6.),1.540);
    return min(plane,sphere) ;
}

//法线函数
vec3 getNormal(vec3 p,float r){
    float e = 0.0001;
    
    vec3 n = normalize(
        vec3(
        getDistance(p+vec3(e,0,0),r)-getDistance(p-vec3(e,0,0),r),
        getDistance(p+vec3(0,e,0),r)-getDistance(p-vec3(0,e,0),r),
        getDistance(p+vec3(0,0,e),r)-getDistance(p-vec3(0,0,e),r)
            
            ));
    return n;
}

float rayMaching(vec3 eyedrection,vec3 eyePosition){
    float d = START_POSITION;
    
    for(int i = 0;i<MAX_ITERATIO_NNUMBER;i++){
        vec3 p =eyePosition+ d* eyedrection;
      
        float newd  = getDistance(p ,2.);
        d+=newd;
        if(newd<MIN_DISTANCE||d>END_POSITION){
            break;
        }
    }
     return d ;
}


void main() {
    vec2 uv = (gl_FragCoord.xy*2.-u_resolution.xy)/min(u_resolution.x,u_resolution.y);
    vec3 col = vec3(0.);
    vec3 eyeposition  = vec3(0, 5,1.);
    vec3 eyedrection  =normalize( vec3(uv,-1.));
    float d = rayMaching(eyedrection,eyeposition);
    

    //光线检测后的坐标p
    vec3  p = eyeposition+eyedrection*d;
    vec3 lightPosition = vec3(0, 5, 6);
    //围绕着xz轴旋转
    lightPosition.xz *=r2d(u_time*0.5) ;
    vec3 lightDirection = normalize(lightPosition-p);
    vec3 n = getNormal(p,2.);
    float diff =clamp(dot(n, lightDirection), 0., 1.);
    
    //创造阴影
    float shdowd = rayMaching(lightDirection,p+n*0.01);
    if(shdowd<length(lightPosition-p)){diff *= 0.1;} 
    col = vec3(diff );
    col = pow(col, vec3(3.));    
    gl_FragColor = vec4(col,1.0);
}

创建相机

我们在webgl高级课程中给大家也具体讲过关于创建相机的内容,创建相机总共需要三要素:

1、相机位置。

2、相机上方向。

3、相机看向的点。

这三个元素就能确定空间中相机的姿态。

相机三要素.png

那么我具体来看看在shader中如何创建相机(请注意下面代码中的注释)

mat3 setCamera(vec3 eyeposition,vec3 lookpoint){
    //设置上方向
    vec3 updirection = vec3(0.,1.,0.);
    //设置眼睛朝向方向
    vec3 eyedirection = lookpoint-eyeposition;
     //根据叉乘得到右方向
    vec3 r =normalize(cross(eyedirection,updirection)) ;
    //创建相机矩阵
    return mat3( r ,updirection,eyedirection);
}

冰哥说:

1、关于叉乘,我会在最近写的关于图形学之几何一书中详细介绍。

2、对于初学经常有疑问,为什么相机返回的来的值要是一个3x3矩阵呢?答案也很简单,因为我们要设置相机参数,动态的去改变相机的朝向啦或者相机的位置等参数。因为我们空间中的位置都是(x,y,z)组成,所有我们构建3x3矩阵目的就是和不同的(x,y,z)位置坐标能够计算,得出相机下的坐标。

相机位置运动

所谓相机位置运动,其实就是通过移动相机位置,不断改变场景。

相机平移运动

讲到相机平移,你要假设你手里有个相机,然后给你女朋友照相,忽然有个猛男碰了你一下,相机也跟着你身体向右边平移了。此时你女朋友的脸不在是在照片的正中间,而是向左发生了平移。二者是相反运动的。我们在shader中经常表示为下面的实现:

    vec3 eyeposition  = vec3(0,5.,13.)-vec3(0.5,0.,0.);

我们来看看下面的相机平移动画,体验一下二者的相反方向的运动关系。

相机移动.gif

其他的轴都是类似于x轴平移效果哈。我们继续来看看下面的效果

相机圆周运动

相机圆形运动.gif

   vec3 eyeposition  = vec3(cos(u_time), sin(u_time)+1.5 ,cos(u_time)+8.160) ;

冰哥说:cos、sin函数传入时间,此时的半径R=1,所以根据我们几何关系,随着u_time值的变化,不断发生围绕在(0,0),半径为1的圆周运动。需要注意的是此时我们的y和z要加上一定的值,否则会钻到平面下面或者离着模型太近,出现不显示等问题。

相机视线运动

所谓相机视线运动,其实就是通过移动相机位置和相机看向的点,本质是运动相机的视线,最终使得场景发生变化。

相机平移运动

如果要是实现相机实现运动,本质是改变eyedirection,我们来看下面代码:

    //视点位置改变
   vec3 eyedrection  =normalize( vec3(uv,-1.)+vec3(sin(u_time),0.,0.));

相机旋转运动

圆周运动2.png

相机做圆周运动和前面类似哈,只不过我们封装成了矩阵,我在另外的图形学几何中给大家讲解:

mat3 rotation3dY(float angle) {
  float s = sin(angle);
  float c = cos(angle);

  return mat3(
    c, 0.0, -s,
    0.0, 1.0, 0.0,
    s, 0.0, c
  );
}
    vec3 eyedrection  =normalize( vec3(uv,-1.)*rotation3dY(sin(u_time)*0.8));

冰哥说基于相机位置运动和基于相机旋转运动二者有本质区别,一个是移动相机,但是视点(看向的点)是没有变化的,另一个是而且同时发生变化,引起视线的变化,大家自己对号入座一下,体会一下二者的区别。

总结

目前我们的场景有点丑,光线过于黯淡,后面我们给大家介绍如何自定义光照和穿上衣服!

0 条评论

发布
问题