I’m studying path tracing, at the moment I created a compute shader that aims to calculate the intersection of rays with meshes. However, when I run the code the unity crashes, displaying a graphics card error.
The error is in the detection of intersection with the meshes. When I put the lines of code responsible for writing a new ray.
But I’m not managing to fix it. I also don’t understand if it could be a problem with my video card or my code. I tested it with only 1 mesh triangle and got a lot of lag.
To send the vertices to the video card I did the following. I got the stitches from the scene. Then I took the indices and vertices arrays and merged them with the other meshes.
public void buildObjectList()
{
int indicesCount = 0;
int verticesCount = 0;
foreach (PathTracingObject pto in pathTracingObjects)
{
int[] triangles;
Vector3[] vertices;
triangles = pto.objectMesh.triangles;//Take a sequence of vertices that form a triangle.
vertices = pto.objectMesh.vertices;//Take vertex list
pto._pathTracingObject.indicesOffset = indicesCount;//Offset: Where the list of indices belonging to the object starts. | indicesCount: How many indices were added before these.
pto._pathTracingObject.indicesCount = triangles.Length;//How many indexes does this object have.
foreach (Vector3 vertice in vertices)
this.vertices.Add(vertice);
for (int i = 0; i < triangles.Length; i++)
{
this.indices.Add(triangles[i] + verticesCount + 1);//Variable containing all indices of all objects in the scene. | triangles[i] (take an index) +verticesCount(Add to the amount of vetices already added) + 1
/*Example:
If the previous object has 270 vertices.
The first triangle of the next objects will be connected to the vertices(271, 272, 273) instead of(0,1,2).
+1 because vertex 270 belongs to a different object.*/
}
indicesCount += triangles.Length;//update index
verticesCount += vertices.Length;//update index
}
}
In compute shader:
/*
http://www.graphics.cornell.edu/pubs/1997/MT97.html
https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/barycentric-coordinates
*/
bool IntersectTriangle(Ray ray, float3 vert0, float3 vert1, float3 vert2, inout float t, inout float u, inout float v)
{
float3 edge1 = vert1 - vert0;
float3 edge2 = vert2 - vert0;
float3 pvec = cross(ray.rayDirection, edge2);
float det = dot(edge1, pvec);
if (det < EPSILON)
return false;
float inv_det = 1.0f / det;
float3 tvec = ray.raySource - vert0;
u = dot(tvec, pvec) * inv_det;
if (u < 0.0 || u > 1.0f)
return false;
float3 qvec = cross(tvec, edge1);
v = dot(ray.rayDirection, qvec) * inv_det;
if (v < 0.0 || u + v > 1.0f)
return false;
t = dot(edge2, qvec) * inv_det;
return true;
}
void IntersectMeshObject(pathTracingObject pto, Ray ray, inout RayHit bestHit)
{
for (int i = 0; i < pto.indicesCount; i += 3) {
float3 v0 = (mul(pto.localToWorldMatrix, float4(_Vertices[_Indices[pto.indicesOffset + i]], 1))).xyz;
float3 v1 = (mul(pto.localToWorldMatrix, float4(_Vertices[_Indices[pto.indicesOffset + i + 1]], 1))).xyz;
float3 v2 = (mul(pto.localToWorldMatrix, float4(_Vertices[_Indices[pto.indicesOffset + i + 2]], 1))).xyz;
float t, u, v;
if (IntersectTriangle(ray, v0, v1, v2, t, u, v))
{
if (t > 0 && t < bestHit.hitDistance)
{
/*
bestHit.hitDistance = t; // makes unity crash
bestHit.hitPosition = ray.raySource + t * ray.rayDirection; // makes unity crash
bestHit.hitNormal = normalize(bestHit.hitPosition); // makes unity crash
bestHit.hitAlbedo = float3(1,1,1); // makes unity crash
*/
}
}
Message information: We detected driver timeout on your system. A bug report was generated. this report can help AMD resolve the issue.
Full code:
// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain
#define PI 3.14159265359
#define EPSILON 0.0000001
RWTexture2D<float4> Result;
struct pathTracingObject
{
float4 Position;
int indicesCount;
int indicesOffset;
float4x4 localToWorldMatrix;
};
StructuredBuffer<pathTracingObject> _pathTracingObject;
StructuredBuffer<float3> _Vertices;
StructuredBuffer<int> _Indices;
int _pathTracingObjectCount;
float4x4 _CameraToWorld;
float4x4 _CameraInverseProjection;
struct Ray {
float3 raySource;
float3 rayDirection;
float3 rayColor;
};
Ray CreateRay(float3 source, float3 direction)
{
Ray ray;
ray.raySource = source;
ray.rayDirection = direction;
return ray;
}
Ray CreateCameraRay(float2 uv)
{
float3 source = mul(_CameraToWorld, float4(0.0f, 0.0f, 0.0f, 1.0f)).xyz;
float3 direction = mul(_CameraInverseProjection, float4(uv, 0.0f, 1.0f)).xyz;
direction = mul(_CameraToWorld, float4(direction, 0.0f)).xyz;
direction = normalize(direction);
return CreateRay(source, direction);
}
struct RayHit
{
float3 hitPosition;
float hitDistance;
float3 hitNormal;
float3 hitAlbedo;
};
RayHit CreateRayHit()
{
RayHit hit;
hit.hitPosition = float3(0.0f, 0.0f, 0.0f);
hit.hitDistance = 1.#INF;
hit.hitNormal = float3(0.0f, 0.0f, 0.0f);
hit.hitAlbedo = float3(0.0f, 0.0f, 0.0f);
return hit;
}
/*
http://www.graphics.cornell.edu/pubs/1997/MT97.html
https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/barycentric-coordinates
*/
bool IntersectTriangle(Ray ray, float3 vert0, float3 vert1, float3 vert2, inout float t, inout float u, inout float v)
{
float3 edge1 = vert1 - vert0;
float3 edge2 = vert2 - vert0;
float3 pvec = cross(ray.rayDirection, edge2);
float det = dot(edge1, pvec);
if (det < EPSILON)
return false;
float inv_det = 1.0f / det;
float3 tvec = ray.raySource - vert0;
u = dot(tvec, pvec) * inv_det;
if (u < 0.0 || u > 1.0f)
return false;
float3 qvec = cross(tvec, edge1);
v = dot(ray.rayDirection, qvec) * inv_det;
if (v < 0.0 || u + v > 1.0f)
return false;
t = dot(edge2, qvec) * inv_det;
return true;
}
void IntersectMeshObject(pathTracingObject pto, Ray ray, inout RayHit bestHit)
{
for (int i = 0; i < pto.indicesCount; i += 3) {
float3 v0 = (mul(pto.localToWorldMatrix, float4(_Vertices[_Indices[pto.indicesOffset + i]], 1))).xyz;
float3 v1 = (mul(pto.localToWorldMatrix, float4(_Vertices[_Indices[pto.indicesOffset + i + 1]], 1))).xyz;
float3 v2 = (mul(pto.localToWorldMatrix, float4(_Vertices[_Indices[pto.indicesOffset + i + 2]], 1))).xyz;
float t, u, v;
if (IntersectTriangle(ray, v0, v1, v2, t, u, v))
{
if (t > 0 && t < bestHit.hitDistance)
{
bestHit.hitDistance = t; // makes unity crash
bestHit.hitPosition = ray.raySource + t * ray.rayDirection; // makes unity crash
bestHit.hitNormal = normalize(bestHit.hitPosition); // makes unity crash
bestHit.hitAlbedo = float3(1,1,1); // makes unity crash
}
}
}
}
RayHit Trace(Ray ray)
{
RayHit bestHit = CreateRayHit();
for (int i = 0; i < _pathTracingObjectCount; i++) {
pathTracingObject pto = _pathTracingObject[i];
IntersectMeshObject(pto, ray, bestHit);
}
return bestHit;
}
[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
// TODO: insert actual code here!
float3 result = float3(0, 0, 0);
int width, height;
Result.GetDimensions(width, height);
float2 uv = float2((id.xy + float2(0.5f, 0.5f)) / float2(width, height) * 2.0f - 1.0f);
for(int i = 0; i < 1;i++) {
Ray ray = CreateCameraRay(id.xy);
RayHit hit = Trace(ray);
result += hit.hitAlbedo;
}
result /= float3(5,5,5);
Result[id.xy] = float4(result, 1);
}
C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
[System.Serializable]
class PathTracing
{
[SerializeField]
ComputeShader computeShader;
[SerializeField]
GameObject[] pathTracingObjects;
class PathTracingComputeShader
{
PathTracingObject[] pathTracingObjects;
public List<Vector3> vertices = new List<Vector3>();
public List<int> indices = new List<int>();
RenderTexture RT;
public void loadRenderTexture()
{
RT = new RenderTexture(Mathf.CeilToInt(Screen.width / 4), Mathf.CeilToInt(Screen.height / 4), 0, RenderTextureFormat.DefaultHDR);
RT.enableRandomWrite = true;
RT.Create();
}
public RenderTexture OnRenderComputeShader(ComputeShader computeShader)
{
loadRenderTexture();
#region Mesh Forwarding
computeShader.SetInt("_pathTracingObjectCount", pathTracingObjects.Length);
foreach (PathTracingObject pto in pathTracingObjects)
pto.OnExecutePathTracing();
buildObjectList();
ComputeBuffer objectsBuffer = new ComputeBuffer(pathTracingObjects.Length, 88) { name = "Scenes Objects Buffer" };
ComputeBuffer vertexBuffer = new ComputeBuffer(vertices.Count, 12) { name = "Vertices Buffer" };
ComputeBuffer indexBuffer = new ComputeBuffer(indices.Count, 4) { name = "Indices Buffer" };
PathTracingObject.pathTracingObject[] sceneObjects = new PathTracingObject.pathTracingObject[pathTracingObjects.Length];
for (int i = 0; i < pathTracingObjects.Length; i++)
{
sceneObjects[i] = pathTracingObjects[i]._pathTracingObject;
}
objectsBuffer.SetData(sceneObjects);
vertexBuffer.SetData(vertices);
indexBuffer.SetData(indices);
computeShader.SetBuffer(0, "_pathTracingObject", objectsBuffer);
computeShader.SetBuffer(0, "_Vertices", vertexBuffer);
computeShader.SetBuffer(0, "_Indices", indexBuffer);
#endregion
computeShader.SetMatrix("_CameraToWorld", Camera.main.cameraToWorldMatrix);
computeShader.SetMatrix("_CameraInverseProjection", Camera.main.projectionMatrix.inverse);
computeShader.SetTexture(0, "Result", RT);
computeShader.Dispatch(0, RT.width, RT.height, 24);
return RT;
}
public void buildObjectList()
{
int indicesCount = 0;
int verticesCount = 0;
foreach (PathTracingObject pto in pathTracingObjects)
{
int[] triangles;
Vector3[] vertices;
triangles = pto.objectMesh.triangles;//Take a sequence of vertices that form a triangle.
vertices = pto.objectMesh.vertices;//Take vertex list
pto._pathTracingObject.indicesOffset = indicesCount;//Offset: Where the list of indices belonging to the object starts. | indicesCount: How many indices were added before these.
pto._pathTracingObject.indicesCount = triangles.Length;//How many indexes does this object have.
foreach (Vector3 vertice in vertices)
this.vertices.Add(vertice);
for (int i = 0; i < triangles.Length; i++)
{
this.indices.Add(triangles[i] + verticesCount + 1);//Variable containing all indices of all objects in the scene. | triangles[i] (take an index) +verticesCount(Add to the amount of vetices already added) + 1
/*Example:
If the previous object has 270 vertices.
The first triangle of the next objects will be connected to the vertices(271, 272, 273) instead of(0,1,2).
+1 because vertex 270 belongs to a different object.*/
}
indicesCount += triangles.Length;//update index
verticesCount += vertices.Length;//update index
}
}
public void setPathTracingObjects(PathTracingObject[] pto) { pathTracingObjects = pto; }
}
public RenderTexture preProcessing()
{
PathTracingComputeShader pathTracingCompute = new PathTracingComputeShader();
PathTracingObject[] objs = new PathTracingObject[pathTracingObjects.Length];
for (int i = 0; i < pathTracingObjects.Length; i++)
objs[i] = pathTracingObjects[i].GetComponent<PathTracingObject>();
pathTracingCompute.setPathTracingObjects(objs);
return pathTracingCompute.OnRenderComputeShader(computeShader);
}
}
I believe it could be 2 things. Or my video card doesn’t count for work. Or there is a problem with the vertex array. (index out of range maybe?)
But I’m not sure, since it just crashes without an error message or something.
I know that the forum is not of unity, but if you can help me I really appreciate it.