I’ve spent the past few months developing a Optix based fractal renderer for my Major project team (as well as a little pipeline), it’s still unfinished but in the end I hope it has at least some of the following features (not all of them are done yet though):
Editable scene compiled at runtime into a hit function based on a node graph (the runtime compilation/ptx patching is done, but the node graph still needs work and I’ve fallen back to hard coding the scene description for now). Although the node approach likely won’t be 100% as controllable as writing all the clever scene description stuff by hand (like Shadertoy), it would allow me to quickly patch together a cool looking scene.
Passing in data from GUI controls without recompiling the whole hit function, maybe adding in the ability to keyframe values or derive their values per frame from Disney’s SeExpressions.
Path Traced Lighting (Done, but looks very odd on my fractal geo, possibly related to me not moving ‘out’ when I hit before firing a shadow ray)
Exporting to .exr with handy render passes (Done, OpenImageIO is great for this) – Here’s the output (my normal channel ends up a bit funky, likely related to the same issue that is causing the broken path tracing)
Current State
Although there’s still much to be done (outputting animating fractals for instance), this is the type of fractal I can currently render (with a little comp, thanks to the handy render passes):
Movement Test
The fractal in my major piece has to be moving, specifically some form of tunnel. Given the current state of the renderer, I had to do a separate test just to see how the fractals could move.
I’ve been looking into using Nvidia Optix for my Fractal lookdev tool, as I need realtime feedback while traversing the landscape.
I really like the API so far, it even comes with a Julia example demo in the SDK! I’ve modified it to render the Mandelbulb instead, with little trouble (and with refraction).
However, I needed to read the buffer data to write .exr’s to disk (this is a vital feature, better to make sure it’s viable at the beginning of the project). That shouldn’t be a problem, as I can easily grab the buffer’s device pointer and then cudaMemcpy it over.
All I need to do is include and , no big deal right?
Linking CXX executable ../bin/julia
CMakeFiles/julia.dir/julia.cpp.o: In function `AnimCamera::apply(optix::Handle<optix::ContextObj>)':
/home/tom/src/optix/SDK/julia/julia.cpp:151: undefined reference to `PinholeCamera::PinholeCamera(float3, float3, float3, float, float, PinholeCamera::AspectRatioMode)'
/home/tom/src/optix/SDK/julia/julia.cpp:153: undefined reference to `PinholeCamera::getEyeUVW(float3&, float3&, float3&, float3&)'
collect2: error: ld returned 1 exit status
Oh.
Well maybe it’s because I included them before OptiX.
#include <optixu/optixpp_namespace.h>
#include <optixu/optixu_math_namespace.h>
#include <cuda.h>
#include <cuda_runtime.h>
using namespace optix;
/usr/local/cuda-7.0/include/cuda_runtime_api.h:257:17: error: ‘cudaError_t’ does not name a type
extern __host__ cudaError_t CUDARTAPI cudaDeviceReset(void);
^
/usr/local/cuda-7.0/include/cuda_runtime_api.h:274:36: error: ‘cudaError_t’ does not name a type
extern __host__ __cudart_builtin__ cudaError_t CUDARTAPI cudaDeviceSynchronize(void);
^
/usr/local/cuda-7.0/include/cuda_runtime_api.h:349:17: error: ‘cudaError_t’ does not name a type
extern __host__ cudaError_t CUDARTAPI cudaDeviceSetLimit(enum cudaLimit limit, size_t value);
^
...
/home/tom/src/optix/SDK/julia/julia.cpp:299:95: error: ‘cudaMemcpy’ was not declared in this scope
cudaMemcpy( (void*)h_ptr, (void*)d_ptr, sizeof(float) * totalPixels, cudaMemcpyDeviceToHost );
Oh.
This is dire, could it be related to the optix namespace somehow?
Linking CXX executable ../bin/julia
[100%] Built target tutorial
Success! Looks blindly using the optix namespace causes clashes with Cuda, setting it up like I did above fixes that in a janky way. Maybe this is mentioned in the documentation somewhere but I never found any reference to it.
So if you’re getting undefined references when you need cudaMemcpy in an OptiX project, check out your include order and namespace setup (and don’t forget to add ${CUDA_LIBRARIES} to target_link_libraries in the cmake configuration if it’s not there already).
The best solution would be to only bring in the stuff you actually need from the optix namespace, so there’s no namespace clashing at all.
So far OptiX seems pretty solid though, I hope I’ll have fun with it!
Update
Turns out in this particular instance I was better off using the map() and unmap() functions within the Optix Buffer class instead of cudaMemcpy(), they remove the dependency of including the CUDA headers. However, I would no doubt have run into the same problems using something like the NVRTC headers.
As an excuse to play with WebGL I decided to finally got around to playing with the Mandelbrot and Julia set. There’s a live demo at the end of the post, the zoom control/mouse movement needs some work but the basic idea works great.
This one is very similar to the code in Ozone3D’s tutorial, I basically copied it for the most part (there’s only so many ways to skin a cat anyway).
The most notable aspect is MAX_ITER, which I replace with the actual iteration value before I compile the shader code. This was to get around the limitation of most webgl implementations which disallow you from using non-constant loop conditions, as they will be unrolled. This isn’t very convenient for real time display of any possible iteration size, so I simply recompile the shader when the iteration size changes.
Note that the “uniform vec2 c” uniform isn’t used here, it’s just there to keep the uniform layout identical for both shaders (I’m lazy and it’s just a demo).
Finally, I use a 1D texture (passed in through a 2D sampler, but the actual texture is only 1 pixel tall) to lookup the expected colour based on the iteration level.
The base code is identical to the Mandelbrot shader, except now we can pass in the desired complex parameter through c.
I can’t explain the maths as well as the pages I referenced earlier; but the main change is now the smooth UVs of the fullscreen quad are now assigned to Z instead of C, as the complex parameter is now a user defined constant and Z varies.
Use the zoom control to zoom in (this isn’t ideal).
Expect slow down on high iterations.
If you want to tinker around with the code then I apologise for how messy it is, it’s my very first adventure into JavaScript and I kept bolting more stuff onto it until I ended up with this.
ToDo
The Ozone3D page mentions that “a better approach is to use a multipass algorithm.”, which is next in line for this project.
It would also be cool to choose a complex parameter for the Julia set by clicking on the Mandelbrot itself, as both sets are connected.
Unreal Engine 4 works pretty well on the Linux desktop now, but I ran into unexpected problems when trying to run the editor natively on my NVIDIA Optimus equipped laptop. I tried to enable my dedicated gpu by usingoptirunbut the “[VGL] ERROR: Could not open display :8”error message was logged when I tried to load a project from the project browser.
This seems to be because optirun doesn’t like Unreal closing and forking a new editor process when the project is chosen. This is not a problem when opening a .uproject file directly, as nothing is closed and no new editor processes are forked. The problem (and potential solutions) are briefly described here, Bumblebee Documentation.
For most people the primus solution would probably work fine, but I never had any luck in getting primus to work so I had to figure out a tidy workaround for the alternative solution (opening a bash session with optirun and running the editor within that).
In the end I ended up using something like this to open a new optirun powered bash shell, start unreal and then wait until it exits before finally exiting the shell again.
# Where does unreal live
unreal_path=$HOME/src/UnrealEngine/Engine/Binaries/Linux
# Make a temporary file to store our commands in
tmp_file=$(mktemp)
# Open project window
echo "$unreal_path/UE4Editor >/dev/null 2>@1" >> $tmp_file
# Figure out the PID of the new editor instance that the project window spawned
# (there are probably better ways of doing this, like pgrep)
# Tokenize the string
echo "editor_process=\$(ps -ejH | tail -n1)" >> $tmp_file
# We only want the first token from the string so we just grab the first item here
echo "editor_pid=( \$editor_process )" >> $tmp_file
# Wait until the editor closes
echo "while kill -0 \$editor_pid >/dev/null 2>@1; do sleep 1; done" >> $tmp_file
# Clean up and exit
echo "rm -f $tmp_file" >> $tmp_file
echo "exit" >> $tmp_file
# Launch a new optirun powered bash instance and ask it to execute the commands
# in the file
optirun bash --rcfile $tmp_file
My polished solution
Note that I use the fish shell, but the syntax should be simple enough to convert to bash if you feel inspired. Notable features include storing the log file for later reading if necessary, automatically figuring out if we should open a specific project file or the project window and fixing the project file path if it’s ambiguous.
function goUnreal
set -l unreal_path $HOME/src/UnrealEngine/Engine/Binaries/Linux
set -l tmp_file (mktemp)
set -q UNREAL_LOG_FILE; and rm -f $UNREAL_LOG_FILE;
set -U UNREAL_LOG_FILE (mktemp)
echo "source ~/.bashrc" > $tmp_file
echo "PS1=\"[Unreal Shell] ~> \"" > $tmp_file
if count $argv[1] > /dev/null
# Test if project file exists
set -l project_file $argv[1]
if test -e $project_file
# Test if full path exists
set project_file (pwd)/$project_file
if test ! -e $project_file
echo "Project file $project_file doesn't exist"
return 1
end
end
# Open specific uproject
echo "$unreal_path/UE4Editor >$UNREAL_LOG_FILE \"$project_file\" 2>@1" >> $tmp_file
else
# Open project window
echo "$unreal_path/UE4Editor >$UNREAL_LOG_FILE 2>@1" >> $tmp_file
# Figure out the PID of the new editor instance that the project window spawned
# Tokenize the string
echo "editor_process=\$(ps -ejH | tail -n1)" >> $tmp_file
# We only want the first token from the string so we just grab the first item here
echo "editor_pid=( \$editor_process )" >> $tmp_file
# Wait until the editor closes
echo "while kill -0 \$editor_pid >/dev/null 2>@1; do sleep 1; done" >> $tmp_file
end
# Clean up and exit
echo "rm -f $tmp_file" >> $tmp_file
echo "exit" >> $tmp_file
optirun bash --rcfile $tmp_file
end