zoukankan      html  css  js  c++  java
  • 光线步进——RayMarching入门

    入门实现

    先用RayMarching描绘一个球体,最后在进行光照计算
    参考:https://www.shadertoy.com/view/llt3R4

    模拟摄像机射线
    float3 rayDirection(float filedOfView, float2 size, float2 fragCoord){
    float2 xy=fragCoord-size/2;
    float z=size.y/tan(radians(filedOfView)/2.0);
    return normalize(float3(xy,-z));
    }
    1
    2
    3
    4
    5
    首先,把屏幕中心设置为坐标原点(0.0,0.0),射线的z值都是固定的,其中filedOfView可以看成视椎体两条棱的夹角,返回归一化的射线向量。

    对射线进行碰撞检测
    float sphereSDF(float3 samplePoint){
    return length(samplePoint) - 1.0;
    }
    1
    2
    3
    float shortestDistanceToSurface(float3 eye,float3 marchingDirection,float start,float end){
    float depth = start;
    for(int i=0;i<maxMarchingSteps;i++){
    float dist=sphereSDF(eye+depth*marchingDirection);
    if(dist < epsilon){
    return depth;
    }

    depth+=dist;
    if(depth>=end){
    return end;
    }
    }
    return end;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    以eye坐标为起点,沿着模拟射线的方向进行碰撞检测,返回碰撞点的深度,如果到最大深度仍然没有碰撞,则发挥最大深度

    根据深度返回颜色
    float dist=shortestDistanceToSurface(eye,dir,minDist,maxDist);
    if(dist>=maxDist-epsilon){
    return float4(0.0,0.0,0.0,0.0);
    }
    float value=floor(dist*10.0)*_StepValue;
    return float4(1-value,sin(value*10.0),0.0,1.0);
    1
    2
    3
    4
    5
    6
    存在问题
    球体的碰撞检测是比较容易的,如果我们想放一个立方体到“场景”里,怎么搞?

    float cubeSDF(float3 samplePoint){
    float3 d=abs(samplePoint)-float3(0.5,0.5,0.5);
    return length(max(d,0.0));
    }
    1
    2
    3
    4

    感觉绘制什么样的物体并不是特别容易控制,需要使用一些数学手段,真佩服哪些用RayMarching画画的那些老哥。

    计算法线方向
    现在,我们知道顶点坐标,通过计算 xyz 三个方向的差值(梯度),归一化后得到一个近似的法线方向

    float3 normalCalculate(float3 p){
    return normalize(float3(
    sphereSDF(float3(p.x + epsilon, p.y, p.z)) - sphereSDF(float3(p.x - epsilon, p.y, p.z)),
    sphereSDF(float3(p.x, p.y + epsilon, p.z)) - sphereSDF(float3(p.x, p.y - epsilon, p.z)),
    sphereSDF(float3(p.x, p.y, p.z + epsilon)) - sphereSDF(float3(p.x, p.y, p.z - epsilon))
    ));
    }
    1
    2
    3
    4
    5
    6
    7
    可以利用matlab进行检验,绘制一个曲面,计算它的表面法线


    clear;
    [X Y]=meshgrid(-0.5:0.05:0.5, -0.5:0.05:0.5);
    Z=0.25-X.^2-Y.^2;
    vecX=sqrt(((X+0.0001).^2+Y.^2+Z))-sqrt(((X-0.0001).^2+Y.^2+Z));
    vecY=sqrt((X.^2+(Y+0.0001).^2+Z))-sqrt((X.^2+(Y-0.0001).^2+Z));
    %mesh(X,Y,Z);
    quiver(X,Y,vecX,vecY);
    1
    2
    3
    4
    5
    6
    7
    根据法线方向计算光照
    设定光源位置,环境光,漫反射颜色,高光颜色,高光系数等信息,计算光照即可

    代码部分
    Shader "Unlit/RayMarching_1"
    {
    Properties
    {
    _MainTex ("Texture", 2D) = "white" {}
    _StepValue("StepValue",Range(0.001,0.025))=0.001
    _LightPos("LightPos",vector)=(0,0,0,0)
    _AmbientCol("AmbientCol",Color)=(1,1,1,1)
    _DiffuseCol("DiffuseCol",Color)=(1,1,1,1)
    _SpecularCol("SpecularCol",Color)=(1,1,1,1)
    _Gloss("Gloss",Range(0.1,255))=10
    }
    SubShader
    {
    Pass
    {
    ZTest Always Cull Off ZWrite Off
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #include "UnityCG.cginc"

    #define maxMarchingSteps 255
    #define minDist 0.0
    #define maxDist 1000.0
    #define epsilon 0.00001
    #define sizeX (_ScreenParams.x/_ScreenParams.y)

    struct appdata
    {
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    };

    struct v2f
    {
    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;
    };

    sampler2D _MainTex;
    float4 _MainTex_ST;
    float _StepValue;
    float4 _LightPos;
    float4 _AmbientCol;
    float4 _DiffuseCol;
    float4 _SpecularCol;
    float _Gloss;

    float cubeSDF(float3 samplePoint){
    float3 d=abs(samplePoint)-float3(0.5,0.5,0.5);
    return length(max(d,0.0));
    }

    float sphereSDF(float3 samplePoint){
    return length(samplePoint) - 0.7;
    }

    float sceneSDF(float3 samplePoint){
    return sphereSDF(samplePoint);
    }

    float3 rayDirection(float filedOfView, float2 size, float2 fragCoord){
    float2 xy=fragCoord-size/2;
    float z=size.y/tan(radians(filedOfView)/2.0);
    return normalize(float3(xy,-z));
    }

    float shortestDistanceToSurface(float3 eye,float3 marchingDirection,float start,float end){
    float depth = start;
    for(int i=0;i<maxMarchingSteps;i++){
    float dist=sceneSDF(eye+depth*marchingDirection);
    if(dist < epsilon){
    return depth;
    }

    depth+=dist;
    if(depth>=end){
    return end;
    }
    }
    return end;
    }

    float3 normalCalculate(float3 p){
    return normalize(float3(
    sceneSDF(float3(p.x + epsilon, p.y, p.z)) - sceneSDF(float3(p.x - epsilon, p.y, p.z)),
    sceneSDF(float3(p.x, p.y + epsilon, p.z)) - sceneSDF(float3(p.x, p.y - epsilon, p.z)),
    sceneSDF(float3(p.x, p.y, p.z + epsilon)) - sceneSDF(float3(p.x, p.y, p.z - epsilon))
    ));
    }

    float3 LightCalculate(float3 eyePos,float3 pos,float3 lightPos,float3 ambientCol,float3 diffuseColor){
    float3 normal = normalCalculate(pos);
    float3 col=diffuseColor*max(dot(normal,normalize(lightPos-pos)),0);
    col+=ambientCol;
    float3 halfDir = normalize(lightPos-pos + eyePos-pos);
    float3 specular = _SpecularCol.rgb * pow(max(0, dot(normal, halfDir)), _Gloss);
    col+=specular;
    return col;
    }

    v2f vert (appdata v)
    {
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    return o;
    }

    float4 frag (v2f i) : SV_Target
    {
    i.uv.x*=_ScreenParams.x/_ScreenParams.y;
    float3 dir=rayDirection(45.0,float2(sizeX,1.0),i.uv);
    float3 eye= float3(0.0,0.0,5.0);
    float dist=shortestDistanceToSurface(eye,dir,minDist,maxDist);
    if(dist>=maxDist-epsilon){
    return float4(0.0,0.0,0.0,1.0);
    }
    float3 pos=eye+dist*dir;

    float3 col=LightCalculate(eye,pos,_LightPos.xyz,_AmbientCol.xyz,_DiffuseCol.xyz);
    return float4(col,1);
    //return float4(0.5,0.5,0.5,1);
    //float value=floor(dist*10.0)*_StepValue;
    //return float4(sin(value*1000),1-value,0.0,1.0);
    }

    ENDCG
    }
    }
    }

    --------------------- 

  • 相关阅读:
    centos7环境下使用xtrabackup备份mysql8.0自动化脚本
    ECharts series data 数组里面的元素,可以是对象
    Docker for Mac 配置镜像加速器,加快镜像下载速度
    sql函数split在presto、spark、hive中的不同
    字段类型为:map<string,string> 时,查询取值(必须使用单引号)
    vendor模式下get报错:go get: disabled by -mod=vendor,此时go env里 GOFLAGS=" -mod=vendor"
    Etcd使用go module的灾难(包依赖问题)
    Go time.Parse转时间戳为啥会自动加8小时?
    scala 下划线使用指南
    【Spark】遍历DataFrame中的每一行数据
  • 原文地址:https://www.cnblogs.com/hyhy904/p/11026664.html
Copyright © 2011-2022 走看看