文章目录
- CSharp
- Shader
- 运行效果
- 总结
- 主要是步进的步长值有技巧
- 第一次步进
- 第二次步进
- 第三次
- Project
- GGB
自学Raymarching汇总:Unity Shader - Ray Marching Study Summary - 学习汇总
这次Raymarching任务:球体、平板地表的测试
CSharpSphereHolder.cs和上一文一样:https://editor.csdn.net/md/?articleId=105734156
using System.Collections;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
// jave.lin 2020.04.24 - 面板挂载器
public class PlaneHolder : MonoBehaviour
{
public Vector3 pos => transform.position;
public Vector4 n
{
get
{
Vector3 n = transform.up;
Vector4 result = n;
result.w = dist;
return result;
}
}
[Range(0, 200)]
public float dist = 0;
#if UNITY_EDITOR
[Range(0, 10)]
public float drawSize = 5;
private void OnDrawGizmos()
{
Gizmos.color = Color.blue;
Vector3 right = transform.right * drawSize;
Vector3 forward = transform.forward * drawSize;
Vector3 up = transform.up * drawSize;
Vector3 p = transform.position;
Vector3 p0 = p + forward - right;
Vector3 p1 = p + forward + right;
Vector3 p2 = p - forward + right;
Vector3 p3 = p - forward - right;
// plane
Gizmos.DrawLine(p0, p1);
Gizmos.DrawLine(p1, p2);
Gizmos.DrawLine(p2, p3);
Gizmos.DrawLine(p3, p0);
// normal
Gizmos.color = Color.blue;
Gizmos.DrawLine(p, p + up);
}
#endif
}
using UnityEngine;
// jave.lin 2020.04.24 - 球体与平板
public class T2_SimpleSphereAndPlane : MonoBehaviour
{
private int _Ray_hash = Shader.PropertyToID("_Ray");
private int _Sphere_hash = Shader.PropertyToID("_Sphere");
private int _PlanePos_hash = Shader.PropertyToID("_PlanePos");
private int _PlaneNormal_hash = Shader.PropertyToID("_PlaneNormal");
public bool raymarching = true;
public Camera cam;
public Material mat;
public SphereHolder sh;
public PlaneHolder ph;
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
var aspect = cam.aspect; // 宽高比
var near = cam.nearClipPlane; // 近截面距离长度
var rightDir = transform.right; // 相机的右边方向(单位向量)
var upDir = transform.up; // 相机的顶部方向(单位向量)
var forwardDir = transform.forward; // 相机的正前方(单位向量)
// fov = field of view,就是相机的顶面与底面的连接相机作为点的夹角,
// 我们取一半就好,与相机正前方方向的线段 * far就是到达远截面的位置(这条边当做下面的tan公式的邻边使用)
// tan(a) = 对 比 邻 = 对/邻
// 邻边的长度是知道的,就是far值,加上fov * 0.5的角度,就可以求出高度(对边)
// tan(a)=对/邻
// 对=tan(a)*邻
var halfOfHeight = Mathf.Tan(cam.fieldOfView * 0.5f * Mathf.Deg2Rad) * near;
// 剩下要求宽度
// aspect = 宽高比 = 宽/高
// 宽 = aspect * 高
var halfOfWidth = aspect * halfOfHeight;
// 前,上,右的角落偏移向量
var forwardVec = forwardDir * near;
var upVec = upDir * halfOfHeight;
var rightVec = rightDir * halfOfWidth;
// 左下角 bottom left
var bl = forwardVec - upVec - rightVec;
// 左上角 top left
var tl = forwardVec + upVec - rightVec;
// 右上角 top right
var tr = forwardVec + upVec + rightVec;
// 右下角 bottom right
var br = forwardVec - upVec + rightVec;
// 视锥体近截面角落点的射线
var frustumFarCornersRay = Matrix4x4.identity;
// 经shader中顶点颜色赋值后出入到屏幕,可以确定,第0是:左下角,1:左上角,2:右上角,3:右下角
frustumFarCornersRay.SetRow(0, bl);
frustumFarCornersRay.SetRow(1, tl);
frustumFarCornersRay.SetRow(2, tr);
frustumFarCornersRay.SetRow(3, br);
mat.SetMatrix(_Ray_hash, frustumFarCornersRay);
// sphere informations
mat.SetVector(_Sphere_hash, sh.spInfo);
// plane informations
mat.SetVector(_PlanePos_hash, ph.pos);
mat.SetVector(_PlaneNormal_hash, ph.n);
// blit
if (raymarching) Graphics.Blit(source, destination, mat);
else Graphics.Blit(source, destination);
}
}
Shader
主要核心逻辑
/* T2_SimpleSphereAndPlane.cginc
jave.lin 2020.04.24
*/
#include "UnityCG.cginc"
#include "../SDFs.cginc"
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
uint vid : SV_VertexID;
};
struct v2f {
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float3 ray : TEXCOORD1;
};
float4x4 _Ray; // 视锥体角落射线
float4 _Sphere; // 球体信息:.xyz = pos, .w = radius
float3 _PlanePos; // 平板的质心点
float4 _PlaneNormal; // 平板的法线
#define EPSILON 0.01 // 最小接触距离
#define MAX_STEP_TIMES 100 // 最大步进次数
float sceneDF(float3 pos) { // 获取场景中所有几何体当中最近距离的
// 使用自己的 DIY 版
// sphere d
// float sphereDist = length(pos - _Sphere.xyz) - _Sphere.w;
// plane d
// references : Unity Shader - 模型消融/淡入淡出效果
// https://blog.csdn.net/linjf520/article/details/98878168
// float planeDist = dot(pos - _PlanePos, _PlaneNormal.xyz);
// 使用 IQ 大神SDFs版
float sphereDist = sdSphere(pos - _Sphere.xyz, _Sphere.w);
float planeDist = sdPlane(pos - _PlanePos, _PlaneNormal);
return min(sphereDist, planeDist);
}
fixed4 getColor(v2f i) {
float3 ori = i.ray; // 射线起点
float3 dir = normalize(i.ray); // 射线方向
float3 pos; // 当前步进到的位置
float dist; // 当前步进到的最近距离
float d; // 当前最近距离
float far = _ProjectionParams.z; // far
ori += _WorldSpaceCameraPos.xyz; // 偏移,加上相机位置
pos = ori; // 从起点出发
UNITY_LOOP
for (int it = 0; it
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?