Skip to content

Commit

Permalink
more sample refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
natevm committed Dec 22, 2024
1 parent ddd1f41 commit 3e882cb
Show file tree
Hide file tree
Showing 16 changed files with 412 additions and 885 deletions.
26 changes: 2 additions & 24 deletions samples/s2-hitPrograms/s2-0-triangles/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
# MIT License

# Copyright (c) 2022 Nathan V. Morrical

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

embed_devicecode(
OUTPUT_TARGET
s2_0_deviceCode
Expand All @@ -29,8 +7,8 @@ embed_devicecode(
${CMAKE_CURRENT_SOURCE_DIR}/deviceCode.slang
)

add_executable(s2_0_singleTriangle hostCode.cpp)
target_link_libraries(s2_0_singleTriangle
add_executable(s2_0_triangles hostCode.cpp)
target_link_libraries(s2_0_triangles
PRIVATE
s2_0_deviceCode
gprt::gprt
Expand Down
131 changes: 64 additions & 67 deletions samples/s2-hitPrograms/s2-0-triangles/deviceCode.slang
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
// MIT License

// Copyright (c) 2022 Nathan V. Morrical

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "sharedCode.h"

[[vk::push_constant]]
Expand All @@ -29,47 +7,6 @@ struct Payload {
float3 color;
};

inline uint32_t
make_bgra(float3 color) {
color = saturate(pow(color, 1.0 / 2.2)) * 255.0;
return (uint32_t(color.z) << 0) + (uint32_t(color.y) << 8) + (uint32_t(color.x) << 16) + (0xffU << 24);
}

// This ray generation program will kick off the ray tracing process,
// generating rays and tracing them into the world.
//
// The first parameter here is the name of our entry point.
//
// The second is the type and name of the shader record. A shader record
// can be thought of as the parameters passed to this kernel.
[shader("raygeneration")]
void
raygen(uniform RayGenData record) {
Payload payload;
uint2 pixelID = DispatchRaysIndex().xy;
uint2 fbSize = DispatchRaysDimensions().xy;
float2 screen = (float2(pixelID) + float2(.5f, .5f)) / float2(fbSize);

RayDesc rayDesc;
rayDesc.Origin = pc.camera.pos;
rayDesc.Direction = normalize(pc.camera.dir_00 + screen.x * pc.camera.dir_du + screen.y * pc.camera.dir_dv);
rayDesc.TMin = 0.001;
rayDesc.TMax = 10000.0;
RaytracingAccelerationStructure world = gprt::getAccelHandle(record.world);
TraceRay(world, // the tree
RAY_FLAG_FORCE_OPAQUE, // ray flags
0xff, // instance inclusion mask
0, // ray type
1, // number of ray types
0, // miss type
rayDesc, // the ray to trace
payload // the payload IO
);

const int fbOfs = pixelID.x + fbSize.x * pixelID.y;
record.frameBuffer[fbOfs] = gprt::make_bgra(payload.color);
}

// This closest hit program will be called when rays hit triangles.
// Here, we can fetch per-geometry data, process that data, and send
// it back to our ray generation program.
Expand All @@ -87,15 +24,75 @@ raygen(uniform RayGenData record) {
// called "barycentrics", which we use to interpolate per-vertex
// values.
[shader("closesthit")]
void
TriangleMesh(uniform TrianglesGeomData record, inout Payload payload, in float2 bc) {
void TriangleMesh(uniform TrianglesGeomData record, inout Payload payload, in float2 bc) {
payload.color = float3(bc.x, bc.y, 1.0 - (bc.x + bc.y));
}

#define AA 3

// This ray generation program will kick off the ray tracing process,
// generating rays and tracing them into the world.
//
// The first parameter here is the name of our entry point.
//
// The second is the type and name of the shader record. A shader record
// can be thought of as the parameters passed to this kernel.
[shader("raygeneration")]
void raygen(uniform RayGenData record) {
Payload payload;
uint2 pixelID = DispatchRaysIndex().xy;
uint2 iResolution = DispatchRaysDimensions().xy;

RaytracingAccelerationStructure world = gprt::getAccelHandle(record.world);

// camera movement
float an = pc.time;
float3 ro = float3(-4.0 * sin(an), 0.0, -4.0 * cos(an));
float3 ta = float3(0.0, 0.0, 0.0);

// camera matrix
float3 ww = normalize(ta - ro);
float3 uu = normalize(cross(ww, float3(0.0, -1.0, 0.0)));
float3 vv = normalize(cross(uu, ww));

float3 tot = float3(0.0);

for (int m = 0; m < AA; m++)
for (int n = 0; n < AA; n++)
{
// pixel coordinates
float2 o = float2(float(m), float(n)) / float(AA) - 0.5;
float2 p = (2.0 * (pixelID + o) - iResolution.xy) / iResolution.y;

// create view ray
float3 rd = normalize(p.x * uu + p.y * vv + 3.0 * ww);

RayDesc rayDesc;
rayDesc.Origin = ro;
rayDesc.Direction = rd;
rayDesc.TMin = 0.0;
rayDesc.TMax = 10000.0;
TraceRay(world, // the tree
RAY_FLAG_NONE, // ray flags
0xff, // instance inclusion mask
0, // ray type
1, // number of ray types
0, // miss index
rayDesc, // the ray to trace
payload // the payload IO
);

tot += payload.color;
}
tot /= float(AA * AA);

const int fbOfs = pixelID.x + iResolution.x * pixelID.y;
record.frameBuffer[fbOfs] = gprt::make_bgra(tot);
}

// A background with a vignette effect.
[shader("miss")]
void
miss(uniform MissProgData record, inout Payload payload) {
void miss(inout Payload payload) {
float2 resolution = DispatchRaysDimensions().xy;
float2 fragCoord = DispatchRaysIndex().xy;
float2 p = (-resolution.xy + 2.0 * fragCoord) / resolution.y;
Expand Down
148 changes: 34 additions & 114 deletions samples/s2-hitPrograms/s2-0-triangles/hostCode.cpp
Original file line number Diff line number Diff line change
@@ -1,164 +1,84 @@
// This program sets up a single geometric object, a mesh for a cube, and
// its acceleration structure, then ray traces it.

#include <gprt.h> // Public GPRT API
#include "sharedCode.h" // Shared data between host and device

extern GPRTProgram s2_0_deviceCode;

// Vertices are the points that define our triangles
// Vertices defining the triangle
const int NUM_VERTICES = 3;
float3 vertices[NUM_VERTICES] = {
{-1.f, -.5f, 0.f},
{+1.f, -.5f, 0.f},
{0.f, +.5f, 0.f},
};

// Indices connect those vertices together.
// Here, vertex 0 connects to 1, which connects to 2 to form a triangle.
// Indices defining the connections between vertices
const int NUM_INDICES = 1;
int3 indices[NUM_INDICES] = {{0, 1, 2}};

// initial image resolution
// Initial image resolution
const int2 fbSize = {1400, 460};

// final image output
const char *outFileName = "s01-singleTriangle.png";

// Initial camera parameters
float3 lookFrom = {0.f, 0.f, -4.f};
float3 lookAt = {0.f, 0.f, 0.f};
float3 lookUp = {0.f, -1.f, 0.f};
float cosFovy = 0.66f;
// Output file name for the rendered image
const char *outFileName = "s2-0-triangles.png";

int main(int ac, char **av) {
// Create a rendering window
gprtRequestWindow(fbSize.x, fbSize.y, "S01 Single Triangle");

// Initialize GPRT context and modules
GPRTContext context = gprtContextCreate();
GPRTModule module = gprtModuleCreate(context, s2_0_deviceCode);
GPRTRayGenOf<RayGenData> rayGen = gprtRayGenCreate<RayGenData>(context, module, "raygen");
GPRTMissOf<MissProgData> miss = gprtMissCreate<MissProgData>(context, module, "miss");

// First, we need to declare our geometry type.
// This includes all GPU kernels tied to the geometry, as well as the
// parameters passed to the geometry when hit by rays.
GPRTGeomTypeOf<TrianglesGeomData> trianglesGeomType = gprtGeomTypeCreate<TrianglesGeomData>(context, GPRT_TRIANGLES);
// New: Create a "triangle" geometry type and set it's closest-hit program
auto trianglesGeomType = gprtGeomTypeCreate<TrianglesGeomData>(context, GPRT_TRIANGLES);
gprtGeomTypeSetClosestHitProg(trianglesGeomType, 0, module, "TriangleMesh");

// Setup pixel frame buffer
GPRTBufferOf<uint32_t> frameBuffer = gprtDeviceBufferCreate<uint32_t>(context, fbSize.x * fbSize.y);

// Raygen program frame buffer
RayGenData *rayGenData = gprtRayGenGetParameters(rayGen);
rayGenData->frameBuffer = gprtBufferGetDevicePointer(frameBuffer);

// Miss program checkerboard background colors
MissProgData *missData = gprtMissGetParameters(miss);
missData->color0 = float3(0.1f, 0.1f, 0.1f);
missData->color1 = float3(0.0f, 0.0f, 0.0f);

// The vertex and index buffers here define the triangle vertices
// and how those vertices are connected together.
GPRTBufferOf<float3> vertexBuffer = gprtDeviceBufferCreate<float3>(context, NUM_VERTICES, vertices);
GPRTBufferOf<int3> indexBuffer = gprtDeviceBufferCreate<int3>(context, NUM_INDICES, indices);
// Upload vertex and index data to GPU buffers
auto vertexBuffer = gprtDeviceBufferCreate<float3>(context, NUM_VERTICES, vertices);
auto indexBuffer = gprtDeviceBufferCreate<int3>(context, NUM_INDICES, indices);

// Next, we will create an instantiation of our geometry declaration.
GPRTGeomOf<TrianglesGeomData> trianglesGeom = gprtGeomCreate<TrianglesGeomData>(context, trianglesGeomType);
// We use these calls to tell the geometry what buffers store triangle
// indices and vertices
// New: Create geometry instance and set vertex and index buffers
auto trianglesGeom = gprtGeomCreate<TrianglesGeomData>(context, trianglesGeomType);
gprtTrianglesSetVertices(trianglesGeom, vertexBuffer, NUM_VERTICES);
gprtTrianglesSetIndices(trianglesGeom, indexBuffer, NUM_INDICES);

// Once we have our geometry, we need to place that geometry into an
// acceleration structure. These acceleration structures allow rays to
// determine which triangle the ray hits in a sub-linear amount of time.
// This first acceleration structure level is called a bottom level
// acceleration structure, or a BLAS.
// Place the geometry into a bottom-level acceleration structure (BLAS).
// A BLAS organizes triangles into a data structure that allows rays to quickly
// determine potential intersections, significantly speeding up ray tracing by narrowing down
// the search to relevant geometry instead of testing every triangle.
GPRTAccel trianglesAccel = gprtTriangleAccelCreate(context, 1, &trianglesGeom);
gprtAccelBuild(context, trianglesAccel, GPRT_BUILD_MODE_FAST_TRACE_NO_UPDATE);

// We can then make multiple "instances", or copies, of that BLAS in
// a top level acceleration structure, or a TLAS. (we'll cover this later.)
// Rays can only be traced into TLAS, so for now we just make one BLAS
// instance.
// Create a single instance of the BLAS in a top-level acceleration structure (TLAS), required for ray tracing.
// (We'll cover TLAS in more depth later)
gprt::Instance instance = gprtAccelGetInstance(trianglesAccel);
GPRTBufferOf<gprt::Instance> instanceBuffer = gprtDeviceBufferCreate<gprt::Instance>(context, 1, &instance);

auto instanceBuffer = gprtDeviceBufferCreate<gprt::Instance>(context, 1, &instance);
GPRTAccel world = gprtInstanceAccelCreate(context, 1, instanceBuffer);
gprtAccelBuild(context, world, GPRT_BUILD_MODE_FAST_TRACE_NO_UPDATE);

// Here, we place a reference to our TLAS in the ray generation
// Set up ray generation and miss programs
GPRTRayGenOf<RayGenData> rayGen = gprtRayGenCreate<RayGenData>(context, module, "raygen");
GPRTMissOf<void> miss = gprtMissCreate<void>(context, module, "miss");

// New: Here, we place a reference to our TLAS in the ray generation
// kernel's parameters, so that we can access that tree when
// we go to trace our rays.
RayGenData *rayGenData = gprtRayGenGetParameters(rayGen);
rayGenData->world = gprtAccelGetHandle(world);

GPRTBufferOf<uint32_t> frameBuffer = gprtDeviceBufferCreate<uint32_t>(context, fbSize.x * fbSize.y);
rayGenData->frameBuffer = gprtBufferGetDevicePointer(frameBuffer);

// Build the Shader Binding Table (SBT), updating all parameters.
gprtBuildShaderBindingTable(context, GPRT_SBT_ALL);

// Structure of parameters that change each frame. We can edit these
// without rebuilding the shader binding table.
// Main render loop
PushConstants pc;

bool firstFrame = true;
double xpos = 0.f, ypos = 0.f;
double lastxpos, lastypos;
do {
float speed = .001f;
lastxpos = xpos;
lastypos = ypos;
gprtGetCursorPos(context, &xpos, &ypos);
if (firstFrame) {
lastxpos = xpos;
lastypos = ypos;
}
int state = gprtGetMouseButton(context, GPRT_MOUSE_BUTTON_LEFT);

// If we click the mouse, we should rotate the camera
// Here, we implement some simple camera controls
if (state == GPRT_PRESS || firstFrame) {
firstFrame = false;
float4 position = {lookFrom.x, lookFrom.y, lookFrom.z, 1.f};
float4 pivot = {lookAt.x, lookAt.y, lookAt.z, 1.0};
#ifndef M_PI
#define M_PI 3.1415926f
#endif

// step 1 : Calculate the amount of rotation given the mouse movement.
float deltaAngleX = (2 * M_PI / fbSize.x);
float deltaAngleY = (M_PI / fbSize.y);
float xAngle = float(lastxpos - xpos) * deltaAngleX;
float yAngle = float(lastypos - ypos) * deltaAngleY;

// step 2: Rotate the camera around the pivot point on the first axis.
float4x4 rotationMatrixX = math::matrixFromRotation(xAngle, lookUp);
position = (mul(rotationMatrixX, (position - pivot))) + pivot;

// step 3: Rotate the camera around the pivot point on the second axis.
float3 lookRight = cross(lookUp, normalize(pivot - position).xyz());
float4x4 rotationMatrixY = math::matrixFromRotation(yAngle, lookRight);
lookFrom = ((mul(rotationMatrixY, (position - pivot))) + pivot).xyz();

// ----------- compute variable values ------------------
float3 camera_pos = lookFrom;
float3 camera_d00 = normalize(lookAt - lookFrom);
float aspect = float(fbSize.x) / float(fbSize.y);
float3 camera_ddu = cosFovy * aspect * normalize(cross(camera_d00, lookUp));
float3 camera_ddv = cosFovy * normalize(cross(camera_ddu, camera_d00));
camera_d00 -= 0.5f * camera_ddu;
camera_d00 -= 0.5f * camera_ddv;

// ----------- set variables ----------------------------
pc.camera.pos = camera_pos;
pc.camera.dir_00 = camera_d00;
pc.camera.dir_du = camera_ddu;
pc.camera.dir_dv = camera_ddv;
}

// Calls the GPU raygen kernel function
pc.time = float(gprtGetTime(context));
gprtRayGenLaunch2D(context, rayGen, fbSize.x, fbSize.y, pc);

// If a window exists, presents the framebuffer here to that window
gprtBufferPresent(context, frameBuffer);
}
// returns true if "X" pressed or if in "headless" mode
while (!gprtWindowShouldClose(context));

// Save final frame to an image
Expand Down
Loading

0 comments on commit 3e882cb

Please sign in to comment.