CUDA 13.2 samples update (#432)

- Added Python samples for CUDA Python 1.0 release
- Renamed top-level `Samples` directory to `cpp` to accommodate Python samples.
This commit is contained in:
Dheemanth
2026-05-13 15:13:18 -07:00
committed by GitHub
parent a4526d5229
commit aeab82ff30
2072 changed files with 15077 additions and 1970 deletions

View File

@@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/../../../Common"
],
"defines": [],
"compilerPath": "/usr/local/cuda/bin/nvcc",
"cStandard": "gnu17",
"cppStandard": "gnu++14",
"intelliSenseMode": "linux-gcc-x64",
"configurationProvider": "ms-vscode.makefile-tools"
}
],
"version": 4
}

View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"nvidia.nsight-vscode-edition",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools"
]
}

View File

@@ -0,0 +1,95 @@
cmake_minimum_required(VERSION 3.20)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/Modules")
project(smokeParticles LANGUAGES C CXX CUDA)
find_package(CUDAToolkit REQUIRED)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
if(ENABLE_CUDA_DEBUG)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G") # enable cuda-gdb (may significantly affect performance on some targets)
else()
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
endif()
# Include directories and libraries
include_directories(../../../Common)
if(WIN32)
set(PC_GLUT_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../Common")
set(PC_GLUT_LIBRARY_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../Common/lib/x64")
# The GLEW library built on Windows on Arm system is named as glew32.lib/glew32.dll by default.
if(EXISTS "${PC_GLUT_LIBRARY_DIRS}/glew32.lib")
set(GLEW_LIB_NAME "glew32")
else()
set(GLEW_LIB_NAME "glew64")
endif()
endif()
find_package(OpenGL)
find_package(GLUT)
# Source file
if(${OpenGL_FOUND})
if (${GLUT_FOUND})
# Add target for smokeParticles
add_executable(smokeParticles GLSLProgram.cpp ParticleSystem.cpp ParticleSystem_cuda.cu SmokeRenderer.cpp SmokeShaders.cpp framebufferObject.cpp particleDemo.cpp renderbuffer.cpp)
target_compile_options(smokeParticles PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
target_compile_features(smokeParticles PRIVATE cxx_std_17 cuda_std_17)
set_target_properties(smokeParticles PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
target_include_directories(smokeParticles PUBLIC
${OPENGL_INCLUDE_DIR}
${CUDAToolkit_INCLUDE_DIRS}
${GLUT_INCLUDE_DIRS}
)
target_link_libraries(smokeParticles
${OPENGL_LIBRARIES}
${GLUT_LIBRARIES}
)
add_custom_command(TARGET smokeParticles POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/data
${CMAKE_CURRENT_BINARY_DIR}/data
)
if(WIN32)
target_link_libraries(smokeParticles
${PC_GLUT_LIBRARY_DIRS}/freeglut.lib
${PC_GLUT_LIBRARY_DIRS}/${GLEW_LIB_NAME}.lib
)
add_custom_command(TARGET smokeParticles
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/win64/$<CONFIGURATION>/freeglut.dll
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
)
add_custom_command(TARGET smokeParticles
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/win64/$<CONFIGURATION>/${GLEW_LIB_NAME}.dll
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>
)
endif()
else()
message(STATUS "GLUT not found - will not build sample 'smokeParticles'")
endif()
else()
message(STATUS "OpenGL not found - will not build sample 'smokeParticles'")
endif()
# Include installation configuration
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/InstallSamples.cmake)
setup_samples_install()

View File

@@ -0,0 +1,256 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#define HELPERGL_EXTERN_GL_FUNC_IMPLEMENTATION
#include <helper_gl.h>
#include "GLSLProgram.h"
GLSLProgram::GLSLProgram(const char *vsource, const char *fsource) { mProg = compileProgram(vsource, 0, fsource); }
GLSLProgram::GLSLProgram(const char *vsource, const char *gsource, const char *fsource, GLenum gsInput, GLenum gsOutput)
{
mProg = compileProgram(vsource, gsource, fsource, gsInput, gsOutput);
}
GLSLProgram::~GLSLProgram()
{
if (mProg) {
glDeleteProgram(mProg);
}
}
void GLSLProgram::enable() { glUseProgram(mProg); }
void GLSLProgram::disable() { glUseProgram(0); }
void GLSLProgram::setUniform1f(const char *name, float value)
{
GLint loc = glGetUniformLocation(mProg, name);
if (loc >= 0) {
glUniform1f(loc, value);
}
else {
#if _DEBUG
fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
}
}
void GLSLProgram::setUniform2f(const char *name, float x, float y)
{
GLint loc = glGetUniformLocation(mProg, name);
if (loc >= 0) {
glUniform2f(loc, x, y);
}
else {
#if _DEBUG
fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
}
}
void GLSLProgram::setUniform3f(const char *name, float x, float y, float z)
{
GLint loc = glGetUniformLocation(mProg, name);
if (loc >= 0) {
glUniform3f(loc, x, y, z);
}
else {
#if _DEBUG
fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
}
}
void GLSLProgram::setUniform4f(const char *name, float x, float y, float z, float w)
{
GLint loc = glGetUniformLocation(mProg, name);
if (loc >= 0) {
glUniform4f(loc, x, y, z, w);
}
else {
#if _DEBUG
fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
}
}
void GLSLProgram::setUniformMatrix4fv(const GLchar *name, GLfloat *m, bool transpose)
{
GLint loc = glGetUniformLocation(mProg, name);
if (loc >= 0) {
glUniformMatrix4fv(loc, 1, transpose, m);
}
else {
#if _DEBUG
fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
}
}
void GLSLProgram::setUniformfv(const GLchar *name, GLfloat *v, int elementSize, int count)
{
GLint loc = glGetUniformLocation(mProg, name);
if (loc == -1) {
#ifdef _DEBUG
fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
return;
}
switch (elementSize) {
case 1:
glUniform1fv(loc, count, v);
break;
case 2:
glUniform2fv(loc, count, v);
break;
case 3:
glUniform3fv(loc, count, v);
break;
case 4:
glUniform4fv(loc, count, v);
break;
}
}
void GLSLProgram::bindTexture(const char *name, GLuint tex, GLenum target, GLint unit)
{
GLint loc = glGetUniformLocation(mProg, name);
if (loc >= 0) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(target, tex);
glUseProgram(mProg);
glUniform1i(loc, unit);
glActiveTexture(GL_TEXTURE0);
}
else {
#if _DEBUG
fprintf(stderr, "Error binding texture '%s'\n", name);
#endif
}
}
GLuint GLSLProgram::checkCompileStatus(GLuint shader, GLint *status)
{
glGetShaderiv(shader, GL_COMPILE_STATUS, status);
if (!(*status)) {
char log[2048];
int len;
glGetShaderInfoLog(shader, 2048, (GLsizei *)&len, log);
printf("Error: shader(%04d), Info log: %s\n", (int)shader, log);
glDeleteShader(shader);
return 0;
}
return 1;
}
GLuint GLSLProgram::compileProgram(const char *vsource,
const char *gsource,
const char *fsource,
GLenum gsInput,
GLenum gsOutput)
{
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
GLint compiled = 0;
glShaderSource(vertexShader, 1, &vsource, 0);
glShaderSource(fragmentShader, 1, &fsource, 0);
glCompileShader(vertexShader);
if (checkCompileStatus(vertexShader, &compiled) == 0) {
printf("<compileProgram compilation error with vertexShader>:\n");
printf("%s\n", vsource);
return 0;
}
glCompileShader(fragmentShader);
if (checkCompileStatus(fragmentShader, &compiled) == 0) {
printf("<compileProgram compilation error with fragmentShader>:\n");
printf("%s\n", fsource);
return 0;
}
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
if (gsource) {
GLuint geomShader = glCreateShader(GL_GEOMETRY_SHADER_EXT);
glShaderSource(geomShader, 1, &gsource, 0);
glCompileShader(geomShader);
glGetShaderiv(geomShader, GL_COMPILE_STATUS, (GLint *)&compiled);
if (checkCompileStatus(geomShader, &compiled) == 0) {
printf("<compileProgram compilation error with geomShader>:\n");
printf("%s\n", gsource);
return 0;
}
glAttachShader(program, geomShader);
glProgramParameteriEXT(program, GL_GEOMETRY_INPUT_TYPE_EXT, gsInput);
glProgramParameteriEXT(program, GL_GEOMETRY_OUTPUT_TYPE_EXT, gsOutput);
glProgramParameteriEXT(program, GL_GEOMETRY_VERTICES_OUT_EXT, 4);
}
glLinkProgram(program);
// check if program linked
GLint success = 0;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
char temp[1024];
glGetProgramInfoLog(program, 1024, 0, temp);
fprintf(stderr, "Failed to link program:\n%s\n", temp);
glDeleteProgram(program);
program = 0;
exit(EXIT_FAILURE);
}
return program;
}

View File

@@ -0,0 +1,71 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Simple class to contain GLSL shaders/programs
#ifndef GLSL_PROGRAM_H
#define GLSL_PROGRAM_H
#include <stdio.h>
class GLSLProgram
{
public:
// construct program from strings
GLSLProgram(const char *vsource, const char *fsource);
GLSLProgram(const char *vsource,
const char *gsource,
const char *fsource,
GLenum gsInput = GL_POINTS,
GLenum gsOutput = GL_TRIANGLE_STRIP);
~GLSLProgram();
void enable();
void disable();
void setUniform1f(const GLchar *name, GLfloat x);
void setUniform2f(const GLchar *name, GLfloat x, GLfloat y);
void setUniform3f(const char *name, float x, float y, float z);
void setUniform4f(const char *name, float x, float y, float z, float w);
void setUniformfv(const GLchar *name, GLfloat *v, int elementSize, int count = 1);
void setUniformMatrix4fv(const GLchar *name, GLfloat *m, bool transpose);
void bindTexture(const char *name, GLuint tex, GLenum target, GLint unit);
inline GLuint getProgId() { return mProg; }
private:
GLuint checkCompileStatus(GLuint shader, GLint *status);
GLuint compileProgram(const char *vsource,
const char *gsource,
const char *fsource,
GLenum gsInput = GL_POINTS,
GLenum gsOutput = GL_TRIANGLE_STRIP);
GLuint mProg;
};
#endif

View File

@@ -0,0 +1,286 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Class to represent an array in GPU and CPU memory
*/
#include <stdio.h>
#include <stdlib.h>
#define HELPERGL_EXTERN_GL_FUNC_IMPLEMENTATION
#include <cuda_gl_interop.h>
#include <cuda_runtime.h>
#include <helper_cuda.h>
#include <helper_gl.h>
template <class T> class GpuArray
{
public:
GpuArray();
~GpuArray();
enum Direction {
HOST_TO_DEVICE,
DEVICE_TO_HOST,
};
// allocate and free
void alloc(size_t size, bool vbo = false, bool doubleBuffer = false, bool useElementArray = false);
void free();
// swap buffers for double buffering
void swap();
// when using vbo, must map before getting device ptr
void map();
void unmap();
void copy(Direction dir, uint start = 0, uint count = 0);
void memset(T value, uint start = 0, uint count = 0);
T *getDevicePtr() { return m_dptr[m_currentRead]; }
GLuint getVbo() { return m_vbo[m_currentRead]; }
T *getDeviceWritePtr() { return m_dptr[m_currentWrite]; }
GLuint getWriteVbo() { return m_vbo[m_currentWrite]; }
T *getHostPtr() { return m_hptr; }
size_t getSize() const { return m_size; }
private:
GLuint createVbo(size_t size, bool useElementArray);
void allocDevice();
void allocVbo(bool useElementArray);
void allocHost();
void freeDevice();
void freeVbo();
void freeHost();
size_t m_size;
T *m_dptr[2];
GLuint m_vbo[2];
struct cudaGraphicsResource *m_cuda_vbo_resource[2]; // handles OpenGL-CUDA exchange
T *m_hptr;
bool m_useVBO;
bool m_doubleBuffer;
uint m_currentRead, m_currentWrite;
};
template <class T>
GpuArray<T>::GpuArray()
: m_size(0)
, m_hptr(0)
, m_currentRead(0)
, m_currentWrite(0)
{
m_dptr[0] = 0;
m_dptr[1] = 0;
m_vbo[0] = 0;
m_vbo[1] = 0;
m_cuda_vbo_resource[0] = NULL;
m_cuda_vbo_resource[1] = NULL;
}
template <class T> GpuArray<T>::~GpuArray() { free(); }
template <class T> void GpuArray<T>::alloc(size_t size, bool vbo, bool doubleBuffer, bool useElementArray)
{
m_size = size;
m_useVBO = vbo;
m_doubleBuffer = doubleBuffer;
if (m_doubleBuffer) {
m_currentWrite = 1;
}
allocHost();
if (vbo) {
allocVbo(useElementArray);
}
else {
allocDevice();
}
}
template <class T> void GpuArray<T>::free()
{
freeHost();
if (m_vbo) {
freeVbo();
}
if (m_dptr) {
freeDevice();
}
}
template <class T> void GpuArray<T>::allocHost() { m_hptr = (T *)new T[m_size]; }
template <class T> void GpuArray<T>::freeHost()
{
if (m_hptr) {
delete[] m_hptr;
m_hptr = 0;
}
}
template <class T> void GpuArray<T>::allocDevice()
{
checkCudaErrors(cudaMalloc((void **)&m_dptr[0], m_size * sizeof(T)));
if (m_doubleBuffer) {
checkCudaErrors(cudaMalloc((void **)&m_dptr[1], m_size * sizeof(T)));
}
}
template <class T> void GpuArray<T>::freeDevice()
{
if (m_dptr[0]) {
checkCudaErrors(cudaFree(m_dptr[0]));
m_dptr[0] = 0;
}
if (m_dptr[1]) {
checkCudaErrors(cudaFree(m_dptr[1]));
m_dptr[1] = 0;
}
}
template <class T> GLuint GpuArray<T>::createVbo(size_t size, bool useElementArray)
{
GLuint vbo;
glGenBuffers(1, &vbo);
if (useElementArray) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
else {
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
return vbo;
}
template <class T> void GpuArray<T>::allocVbo(bool useElementArray)
{
m_vbo[0] = createVbo(m_size * sizeof(T), useElementArray);
checkCudaErrors(cudaGraphicsGLRegisterBuffer(&m_cuda_vbo_resource[0], m_vbo[0], cudaGraphicsMapFlagsWriteDiscard));
if (m_doubleBuffer) {
m_vbo[1] = createVbo(m_size * sizeof(T), useElementArray);
checkCudaErrors(
cudaGraphicsGLRegisterBuffer(&m_cuda_vbo_resource[1], m_vbo[1], cudaGraphicsMapFlagsWriteDiscard));
}
}
template <class T> void GpuArray<T>::freeVbo()
{
if (m_vbo[0]) {
checkCudaErrors(cudaGraphicsUnregisterResource(m_cuda_vbo_resource[0]));
glDeleteBuffers(1, &m_vbo[0]);
m_vbo[0] = 0;
}
if (m_vbo[1]) {
checkCudaErrors(cudaGraphicsUnregisterResource(m_cuda_vbo_resource[1]));
glDeleteBuffers(1, &m_vbo[1]);
m_vbo[1] = 0;
}
}
template <class T> void GpuArray<T>::swap() { std::swap(m_currentRead, m_currentWrite); }
template <class T> void GpuArray<T>::map()
{
if (m_vbo[0]) {
checkCudaErrors(cudaGraphicsMapResources(1, &m_cuda_vbo_resource[0], 0));
size_t num_bytes;
checkCudaErrors(cudaGraphicsResourceGetMappedPointer((void **)&m_dptr[0], &num_bytes, m_cuda_vbo_resource[0]));
}
if (m_doubleBuffer && m_vbo[1]) {
checkCudaErrors(cudaGraphicsMapResources(1, &m_cuda_vbo_resource[1], 0));
size_t num_bytes;
checkCudaErrors(cudaGraphicsResourceGetMappedPointer((void **)&m_dptr[1], &num_bytes, m_cuda_vbo_resource[1]));
}
}
template <class T> void GpuArray<T>::unmap()
{
if (m_vbo[0]) {
checkCudaErrors(cudaGraphicsUnmapResources(1, &m_cuda_vbo_resource[0], 0));
m_dptr[0] = 0;
}
if (m_doubleBuffer && m_vbo[1]) {
checkCudaErrors(cudaGraphicsUnmapResources(1, &m_cuda_vbo_resource[1], 0));
m_dptr[1] = 0;
}
}
template <class T> void GpuArray<T>::copy(Direction dir, uint start, uint count)
{
if (count == 0) {
count = (uint)m_size;
}
map();
switch (dir) {
case HOST_TO_DEVICE:
checkCudaErrors(cudaMemcpy((void *)(m_dptr[m_currentRead] + start),
(void *)(m_hptr + start),
count * sizeof(T),
cudaMemcpyHostToDevice));
break;
case DEVICE_TO_HOST:
checkCudaErrors(cudaMemcpy((void *)(m_hptr + start),
(void *)(m_dptr[m_currentRead] + start),
count * sizeof(T),
cudaMemcpyDeviceToHost));
break;
}
unmap();
}
template <class T> void GpuArray<T>::memset(T value, uint start, uint count) {}

View File

@@ -0,0 +1,402 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <algorithm>
#include <assert.h>
#include <cstdio>
#include <cstdlib>
#include <math.h>
#include <memory.h>
#define HELPERGL_EXTERN_GL_FUNC_IMPLEMENTATION
// includes for OpenGL
#include <helper_gl.h>
// includes
#include <cuda_gl_interop.h>
#include <cuda_runtime.h>
#include <helper_cuda.h>
#include <helper_functions.h>
#include "ParticleSystem.cuh"
#include "ParticleSystem.h"
#include "particles_kernel.cuh"
#ifndef CUDART_PI_F
#define CUDART_PI_F 3.141592654f
#endif
/*
This handles the particle simulation using CUDA
*/
ParticleSystem::ParticleSystem(uint numParticles, bool bUseVBO, bool bUseGL)
: m_bInitialized(false)
, m_bUseVBO(bUseVBO)
, m_numParticles(numParticles)
, m_particleRadius(0.1f)
, m_doDepthSort(false)
, m_timer(NULL)
, m_time(0.0f)
{
m_params.gravity = make_float3(0.0f, 0.0f, 0.0f);
m_params.globalDamping = 1.0f;
m_params.noiseSpeed = make_float3(0.0f, 0.0f, 0.0f);
_initialize(numParticles, bUseGL);
}
ParticleSystem::~ParticleSystem()
{
_free();
m_numParticles = 0;
}
void ParticleSystem::_initialize(int numParticles, bool bUseGL)
{
assert(!m_bInitialized);
createNoiseTexture(64, 64, 64);
m_numParticles = numParticles;
// allocate GPU arrays
m_pos.alloc(m_numParticles, m_bUseVBO, true); // create as VBO
m_vel.alloc(m_numParticles, m_bUseVBO, true);
m_sortKeys.alloc(m_numParticles);
m_indices.alloc(m_numParticles, m_bUseVBO, false,
true); // create as index buffer
sdkCreateTimer(&m_timer);
setParameters(&m_params);
m_bInitialized = true;
}
void ParticleSystem::_free() { assert(m_bInitialized); }
// step the simulation
void ParticleSystem::step(float deltaTime)
{
assert(m_bInitialized);
m_params.time = m_time;
setParameters(&m_params);
m_pos.map();
m_vel.map();
// integrate particles
integrateSystem(m_pos.getDevicePtr(),
m_pos.getDeviceWritePtr(),
m_vel.getDevicePtr(),
m_vel.getDeviceWritePtr(),
deltaTime,
m_numParticles);
m_pos.unmap();
m_vel.unmap();
m_pos.swap();
m_vel.swap();
m_time += deltaTime;
}
// depth sort the particles
void ParticleSystem::depthSort()
{
if (!m_doDepthSort) {
return;
}
m_pos.map();
m_indices.map();
// calculate depth
calcDepth(m_pos.getDevicePtr(), m_sortKeys.getDevicePtr(), m_indices.getDevicePtr(), m_sortVector, m_numParticles);
// radix sort
sortParticles(m_sortKeys.getDevicePtr(), m_indices.getDevicePtr(), m_numParticles);
m_pos.unmap();
m_indices.unmap();
}
uint *ParticleSystem::getSortedIndices()
{
// copy sorted indices back to CPU
m_indices.copy(GpuArray<uint>::DEVICE_TO_HOST);
return m_indices.getHostPtr();
}
// random float [0, 1]
inline float frand() { return rand() / (float)RAND_MAX; }
// signed random float [-1, 1]
inline float sfrand() { return frand() * 2.0f - 1.0f; }
// random signed vector
inline vec3f svrand() { return vec3f(sfrand(), sfrand(), sfrand()); }
// random point in circle
inline vec2f randCircle()
{
vec2f r;
do {
r = vec2f(sfrand(), sfrand());
} while (length(r) > 1.0f);
return r;
}
// random point in sphere
inline vec3f randSphere()
{
vec3f r;
do {
r = svrand();
} while (length(r) > 1.0f);
return r;
}
// initialize in regular grid
void ParticleSystem::initGrid(vec3f start,
uint3 size,
vec3f spacing,
float jitter,
vec3f vel,
uint numParticles,
float lifetime)
{
srand(1973);
float4 *posPtr = m_pos.getHostPtr();
float4 *velPtr = m_vel.getHostPtr();
for (uint z = 0; z < size.z; z++) {
for (uint y = 0; y < size.y; y++) {
for (uint x = 0; x < size.x; x++) {
uint i = (z * size.y * size.x) + (y * size.x) + x;
if (i < numParticles) {
vec3f pos = start + spacing * vec3f((float)x, (float)y, (float)z) + svrand() * jitter;
posPtr[i] = make_float4(pos.x, pos.y, pos.z, 0.0f);
velPtr[i] = make_float4(vel.x, vel.y, vel.z, lifetime);
}
}
}
}
}
// initialize in random positions within cube
void ParticleSystem::initCubeRandom(vec3f origin, vec3f size, vec3f vel, float lifetime)
{
float4 *posPtr = m_pos.getHostPtr();
float4 *velPtr = m_vel.getHostPtr();
for (uint i = 0; i < m_numParticles; i++) {
vec3f pos = origin + svrand() * size;
posPtr[i] = make_float4(pos.x, pos.y, pos.z, 0.0f);
velPtr[i] = make_float4(vel.x, vel.y, vel.z, lifetime);
}
}
// add sphere on regular grid
void ParticleSystem::addSphere(uint &index, vec3f pos, vec3f vel, int r, float spacing, float jitter, float lifetime)
{
float4 *posPtr = m_pos.getHostPtr();
float4 *velPtr = m_vel.getHostPtr();
uint start = index;
uint count = 0;
for (int z = -r; z <= r; z++) {
for (int y = -r; y <= r; y++) {
for (int x = -r; x <= r; x++) {
vec3f delta = vec3f((float)x, (float)y, (float)z) * spacing;
float dist = length(delta);
if ((dist <= spacing * r) && (index < m_numParticles)) {
// vec3f p = pos + delta + svrand()*jitter;
posPtr[index] = make_float4(pos.x, pos.y, pos.z, 0.0f);
velPtr[index] = make_float4(vel.x, vel.y, vel.z, lifetime);
index++;
count++;
}
}
}
}
m_pos.copy(GpuArray<float4>::HOST_TO_DEVICE, start, count);
m_vel.copy(GpuArray<float4>::HOST_TO_DEVICE, start, count);
}
void ParticleSystem::reset(ParticleConfig config)
{
switch (config) {
default:
case CONFIG_RANDOM:
initCubeRandom(vec3f(0.0, 1.0, 0.0), vec3f(1.0, 1.0, 1.0), vec3f(0.0f), 100.0);
break;
case CONFIG_GRID: {
float jitter = m_particleRadius * 0.01f;
uint s = (int)ceilf(powf((float)m_numParticles, 1.0f / 3.0f));
uint gridSize[3];
gridSize[0] = gridSize[1] = gridSize[2] = s;
initGrid(vec3f(-1.0, 0.0, -1.0),
make_uint3(s, s, s),
vec3f(m_particleRadius * 2.0f),
jitter,
vec3f(0.0),
m_numParticles,
100.0);
} break;
}
m_pos.copy(GpuArray<float4>::HOST_TO_DEVICE);
m_vel.copy(GpuArray<float4>::HOST_TO_DEVICE);
}
// particle emitters
void ParticleSystem::discEmitter(uint &index,
vec3f pos,
vec3f vel,
vec3f vx,
vec3f vy,
float r,
int n,
float lifetime,
float lifetimeVariance)
{
float4 *posPtr = m_pos.getHostPtr();
float4 *velPtr = m_vel.getHostPtr();
uint start = index;
uint count = 0;
for (int i = 0; i < n; i++) {
vec2f delta = randCircle() * r;
if (index < m_numParticles) {
vec3f p = pos + delta.x * vx + delta.y * vy;
float lt = lifetime + frand() * lifetimeVariance;
posPtr[index] = make_float4(p.x, p.y, p.z, 0.0f);
velPtr[index] = make_float4(vel.x, vel.y, vel.z, lt);
index++;
count++;
}
}
m_pos.copy(GpuArray<float4>::HOST_TO_DEVICE, start, count);
m_vel.copy(GpuArray<float4>::HOST_TO_DEVICE, start, count);
}
void ParticleSystem::sphereEmitter(uint &index,
vec3f pos,
vec3f vel,
vec3f spread,
float r,
int n,
float lifetime,
float lifetimeVariance)
{
float4 *posPtr = m_pos.getHostPtr();
float4 *velPtr = m_vel.getHostPtr();
uint start = index;
uint count = 0;
for (int i = 0; i < n; i++) {
vec3f x = randSphere();
// float dist = length(x);
if (index < m_numParticles) {
vec3f p = pos + x * r;
float age = 0.0;
float lt = lifetime + frand() * lifetimeVariance;
vec3f dir = randSphere();
dir.y = fabs(dir.y);
vec3f v = vel + dir * spread;
posPtr[index] = make_float4(p.x, p.y, p.z, age);
velPtr[index] = make_float4(v.x, v.y, v.z, lt);
index++;
count++;
}
}
m_pos.copy(GpuArray<float4>::HOST_TO_DEVICE, start, count);
m_vel.copy(GpuArray<float4>::HOST_TO_DEVICE, start, count);
}
void ParticleSystem::setModelView(float *m)
{
for (int i = 0; i < 16; i++) {
m_modelView.m[i] = m[i];
}
}
// dump particles to stdout for debugging
void ParticleSystem::dumpParticles(uint start, uint count)
{
m_pos.copy(GpuArray<float4>::DEVICE_TO_HOST);
float4 *pos = m_pos.getHostPtr();
m_vel.copy(GpuArray<float4>::DEVICE_TO_HOST);
float4 *vel = m_vel.getHostPtr();
for (uint i = start; i < start + count; i++) {
printf("%d: ", i);
printf("pos: (%.4f, %.4f, %.4f, %.4f)\n", pos[i].x, pos[i].y, pos[i].z, pos[i].w);
printf("vel: (%.4f, %.4f, %.4f, %.4f)\n", vel[i].x, vel[i].y, vel[i].z, vel[i].w);
}
}
// dump particles to a system memory host
void ParticleSystem::dumpBin(float4 **posData, float4 **velData)
{
m_pos.copy(GpuArray<float4>::DEVICE_TO_HOST);
*posData = m_pos.getHostPtr();
m_vel.copy(GpuArray<float4>::DEVICE_TO_HOST);
*velData = m_vel.getHostPtr();
}

View File

@@ -0,0 +1,46 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "particles_kernel.cuh"
extern "C"
{
void initCuda(bool bUseGL);
void setParameters(SimParams *hostParams);
void createNoiseTexture(int w, int h, int d);
void
integrateSystem(float4 *oldPos, float4 *newPos, float4 *oldVel, float4 *newVel, float deltaTime, int numParticles);
void calcDepth(float4 *pos,
float *keys, // output
uint *indices, // output
float3 sortVector,
int numParticles);
void sortParticles(float *sortKeys, uint *indices, uint numParticles);
}

View File

@@ -0,0 +1,130 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __PARTICLESYSTEM_H__
#define __PARTICLESYSTEM_H__
#include <helper_functions.h>
#include "GpuArray.h"
#include "nvMath.h"
#include "particles_kernel.cuh"
#include "vector_functions.h"
using namespace nv;
// CUDA BodySystem: runs on the GPU
class ParticleSystem
{
public:
ParticleSystem(uint numParticles, bool bUseVBO = true, bool bUseGL = true);
~ParticleSystem();
enum ParticleConfig { CONFIG_RANDOM, CONFIG_GRID, _NUM_CONFIGS };
void step(float deltaTime);
void depthSort();
void reset(ParticleConfig config);
uint getNumParticles() { return m_numParticles; }
uint getPosBuffer() { return m_pos.getVbo(); }
uint getVelBuffer() { return m_vel.getVbo(); }
uint getColorBuffer() { return 0; }
uint getSortedIndexBuffer() { return m_indices.getVbo(); }
uint *getSortedIndices();
float getParticleRadius() { return m_particleRadius; }
SimParams &getParams() { return m_params; }
void setSorting(bool x) { m_doDepthSort = x; }
void setModelView(float *m);
void setSortVector(float3 v) { m_sortVector = v; }
void addSphere(uint &index, vec3f pos, vec3f vel, int r, float spacing, float jitter, float lifetime);
void discEmitter(uint &index,
vec3f pos,
vec3f vel,
vec3f vx,
vec3f vy,
float r,
int n,
float lifetime,
float lifetimeVariance);
void sphereEmitter(uint &index,
vec3f pos,
vec3f vel,
vec3f spread,
float r,
int n,
float lifetime,
float lifetimeVariance);
void dumpParticles(uint start, uint count);
void dumpBin(float4 **posData, float4 **velData);
protected: // methods
ParticleSystem() {}
void _initialize(int numParticlesm, bool bUseGL = true);
void _free();
void initGrid(vec3f start,
uint3 size,
vec3f spacing,
float jitter,
vec3f vel,
uint numParticles,
float lifetime = 100.0f);
void initCubeRandom(vec3f origin, vec3f size, vec3f vel, float lifetime = 100.0f);
protected: // data
bool m_bInitialized;
bool m_bUseVBO;
uint m_numParticles;
float m_particleRadius;
GpuArray<float4> m_pos;
GpuArray<float4> m_vel;
// params
SimParams m_params;
float4x4 m_modelView;
float3 m_sortVector;
bool m_doDepthSort;
GpuArray<float> m_sortKeys;
GpuArray<uint> m_indices; // sorted indices for rendering
StopWatchInterface *m_timer;
float m_time;
};
#endif // __PARTICLESYSTEM_H__

View File

@@ -0,0 +1,160 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
This file contains simple wrapper functions that call the CUDA kernels
*/
#define HELPERGL_EXTERN_GL_FUNC_IMPLEMENTATION
// includes for OpenGL
#include <helper_gl.h>
// includes
#include <cstdio>
#include <cstdlib>
#include <cuda_gl_interop.h>
#include <helper_cuda.h>
#include <string.h>
#include "ParticleSystem.cuh"
#include "particles_kernel_device.cuh"
#include "thrust/device_ptr.h"
#include "thrust/for_each.h"
#include "thrust/iterator/zip_iterator.h"
#include "thrust/sort.h"
extern "C"
{
cudaArray *noiseArray;
void setParameters(SimParams *hostParams)
{
// copy parameters to constant memory
checkCudaErrors(cudaMemcpyToSymbol(cudaParams, hostParams, sizeof(SimParams)));
}
// Round a / b to nearest higher integer value
int iDivUp(int a, int b) { return (a % b != 0) ? (a / b + 1) : (a / b); }
// compute grid and thread block size for a given number of elements
void computeGridSize(int n, int blockSize, int &numBlocks, int &numThreads)
{
numThreads = min(blockSize, n);
numBlocks = iDivUp(n, numThreads);
}
inline float frand() { return rand() / (float)RAND_MAX; }
// create 3D texture containing random values
void createNoiseTexture(int w, int h, int d)
{
cudaExtent size = make_cudaExtent(w, h, d);
size_t elements = size.width * size.height * size.depth;
float *volumeData = (float *)malloc(elements * 4 * sizeof(float));
float *ptr = volumeData;
for (size_t i = 0; i < elements; i++) {
*ptr++ = frand() * 2.0f - 1.0f;
*ptr++ = frand() * 2.0f - 1.0f;
*ptr++ = frand() * 2.0f - 1.0f;
*ptr++ = frand() * 2.0f - 1.0f;
}
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float4>();
checkCudaErrors(cudaMalloc3DArray(&noiseArray, &channelDesc, size));
cudaMemcpy3DParms copyParams = {0};
copyParams.srcPtr =
make_cudaPitchedPtr((void *)volumeData, size.width * sizeof(float4), size.width, size.height);
copyParams.dstArray = noiseArray;
copyParams.extent = size;
copyParams.kind = cudaMemcpyHostToDevice;
checkCudaErrors(cudaMemcpy3D(&copyParams));
free(volumeData);
cudaResourceDesc texRes;
memset(&texRes, 0, sizeof(cudaResourceDesc));
texRes.resType = cudaResourceTypeArray;
texRes.res.array.array = noiseArray;
cudaTextureDesc texDescr;
memset(&texDescr, 0, sizeof(cudaTextureDesc));
texDescr.normalizedCoords = true;
texDescr.filterMode = cudaFilterModeLinear;
texDescr.addressMode[0] = cudaAddressModeWrap;
texDescr.addressMode[1] = cudaAddressModeWrap;
texDescr.addressMode[2] = cudaAddressModeWrap;
texDescr.readMode = cudaReadModeElementType;
checkCudaErrors(cudaCreateTextureObject(&noiseTex, &texRes, &texDescr, NULL));
}
void
integrateSystem(float4 *oldPos, float4 *newPos, float4 *oldVel, float4 *newVel, float deltaTime, int numParticles)
{
thrust::device_ptr<float4> d_newPos(newPos);
thrust::device_ptr<float4> d_newVel(newVel);
thrust::device_ptr<float4> d_oldPos(oldPos);
thrust::device_ptr<float4> d_oldVel(oldVel);
thrust::for_each(
thrust::make_zip_iterator(d_newPos, d_newVel, d_oldPos, d_oldVel),
thrust::make_zip_iterator(
d_newPos + numParticles, d_newVel + numParticles, d_oldPos + numParticles, d_oldVel + numParticles),
integrate_functor(deltaTime, noiseTex));
}
void calcDepth(float4 *pos,
float *keys, // output
uint *indices, // output
float3 sortVector,
int numParticles)
{
thrust::device_ptr<float4> d_pos(pos);
thrust::device_ptr<float> d_keys(keys);
thrust::device_ptr<uint> d_indices(indices);
thrust::for_each(thrust::make_zip_iterator(d_pos, d_keys),
thrust::make_zip_iterator(d_pos + numParticles, d_keys + numParticles),
calcDepth_functor(sortVector));
thrust::sequence(d_indices, d_indices + numParticles);
}
void sortParticles(float *sortKeys, uint *indices, uint numParticles)
{
thrust::sort_by_key(thrust::device_ptr<float>(sortKeys),
thrust::device_ptr<float>(sortKeys + numParticles),
thrust::device_ptr<uint>(indices));
}
} // extern "C"

View File

@@ -0,0 +1,38 @@
# smokeParticles - Smoke Particles
## Description
Smoke simulation with volumetric shadows using half-angle slicing technique. Uses CUDA for procedural simulation, Thrust Library for sorting algorithms, and OpenGL for graphics rendering.
## Key Concepts
Graphics Interop, Data Parallel Algorithms, Physically-Based Simulation
## Supported SM Architectures
[SM 5.0 ](https://developer.nvidia.com/cuda-gpus) [SM 5.2 ](https://developer.nvidia.com/cuda-gpus) [SM 5.3 ](https://developer.nvidia.com/cuda-gpus) [SM 6.0 ](https://developer.nvidia.com/cuda-gpus) [SM 6.1 ](https://developer.nvidia.com/cuda-gpus) [SM 7.0 ](https://developer.nvidia.com/cuda-gpus) [SM 7.2 ](https://developer.nvidia.com/cuda-gpus) [SM 7.5 ](https://developer.nvidia.com/cuda-gpus) [SM 8.0 ](https://developer.nvidia.com/cuda-gpus) [SM 8.6 ](https://developer.nvidia.com/cuda-gpus) [SM 8.7 ](https://developer.nvidia.com/cuda-gpus) [SM 8.9 ](https://developer.nvidia.com/cuda-gpus) [SM 9.0 ](https://developer.nvidia.com/cuda-gpus)
## Supported OSes
Linux, Windows
## Supported CPU Architecture
x86_64, armv7l
## CUDA APIs involved
### [CUDA Runtime API](http://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
cudaExtent, cudaPitchedPtr, cudaCreateTextureObject, cudaMemcpyToSymbol
## Dependencies needed to build/run
[X11](../../../README.md#x11), [OpenGL](../../../README.md#opengl), [Freeglut](../../../README.md#freeglut), [GLEW](../../../README.md#glew)
## Prerequisites
Download and install the [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
## References (for more details)
[whitepaper](./doc/smokeParticles.pdf)

View File

@@ -0,0 +1,584 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
This class renders particles using OpenGL and GLSL shaders
*/
#include <math.h>
#include <stdlib.h>
#define HELPERGL_EXTERN_GL_FUNC_IMPLEMENTATION
#include <helper_gl.h>
#include "SmokeRenderer.h"
#include "SmokeShaders.h"
#if defined(__APPLE__) || defined(MACOSX)
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include <GLUT/glut.h>
#else
#include <GL/freeglut.h>
#endif
#define USE_MBLUR 1
#define COLOR_ATTENUATION 1
SmokeRenderer::SmokeRenderer(int maxParticles)
: mMaxParticles(maxParticles)
, mNumParticles(0)
, mPosVbo(0)
, mVelVbo(0)
, mColorVbo(0)
, mIndexBuffer(0)
, mParticleRadius(0.005f)
, mDisplayMode(VOLUMETRIC)
, mWindowW(800)
, mWindowH(600)
, mFov(40.0f)
, m_downSample(2)
, m_numSlices(32)
, m_numDisplayedSlices(32)
, m_sliceNo(0)
, m_shadowAlpha(0.005f)
, m_spriteAlpha(0.1f)
, m_doBlur(false)
, m_blurRadius(1.0f)
, m_displayLightBuffer(false)
, m_lightPos(5.0f, 5.0f, -5.0f)
, m_lightTarget(0.0f, 0.0f, 0.0f)
, m_lightColor(1.0f, 1.0f, 0.5f)
, m_colorAttenuation(0.1f, 0.2f, 0.3f)
, m_lightBufferSize(256)
, m_srcLightTexture(0)
, m_lightDepthTexture(0)
, m_lightFbo(0)
, m_imageTex(0)
, m_depthTex(0)
, m_imageFbo(0)
{
// load shader programs
m_simpleProg = new GLSLProgram(particleVS, simplePS);
m_particleProg = new GLSLProgram(mblurVS, mblurGS, particlePS);
m_particleShadowProg = new GLSLProgram(mblurVS, mblurGS, particleShadowPS);
m_blurProg = new GLSLProgram(passThruVS, blurPS);
m_displayTexProg = new GLSLProgram(passThruVS, texture2DPS);
// create buffer for light shadows
createLightBuffer();
glutReportErrors();
}
SmokeRenderer::~SmokeRenderer()
{
delete m_particleProg;
delete m_particleShadowProg;
delete m_blurProg;
delete m_displayTexProg;
delete m_simpleProg;
delete m_lightFbo;
glDeleteTextures(2, m_lightTexture);
glDeleteTextures(1, &m_lightDepthTexture);
delete m_imageFbo;
glDeleteTextures(1, &m_imageTex);
glDeleteTextures(1, &m_depthTex);
}
// draw points from vertex buffer objects
void SmokeRenderer::drawPoints(int start, int count, bool sort)
{
glBindBuffer(GL_ARRAY_BUFFER, mPosVbo);
glVertexPointer(4, GL_FLOAT, 0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
if (mColorVbo) {
glBindBuffer(GL_ARRAY_BUFFER, mColorVbo);
glColorPointer(4, GL_FLOAT, 0, 0);
glEnableClientState(GL_COLOR_ARRAY);
}
if (mVelVbo) {
glBindBuffer(GL_ARRAY_BUFFER, mVelVbo);
glClientActiveTexture(GL_TEXTURE0);
glTexCoordPointer(4, GL_FLOAT, 0, 0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
if (sort) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, mIndexBuffer);
glDrawElements(GL_POINTS, count, GL_UNSIGNED_INT, (void *)(start * sizeof(unsigned int)));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
}
else {
glDrawArrays(GL_POINTS, start, count);
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glClientActiveTexture(GL_TEXTURE0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
// draw points using given shader program
void SmokeRenderer::drawPointSprites(GLSLProgram *prog, int start, int count, bool shadowed)
{
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE); // don't write depth
glEnable(GL_BLEND);
prog->enable();
prog->setUniform1f("pointRadius", mParticleRadius);
if (shadowed) {
prog->bindTexture("shadowTex", m_lightTexture[m_srcLightTexture], GL_TEXTURE_2D, 0);
}
// draw points
drawPoints(start, count, true);
prog->disable();
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
}
// calculate vectors for half-angle slice rendering
void SmokeRenderer::calcVectors()
{
// get model view matrix
glGetFloatv(GL_MODELVIEW_MATRIX, (float *)m_modelView.get_value());
// calculate eye space light vector
m_lightVector = normalize(m_lightPos);
m_lightPosEye = m_modelView * vec4f(m_lightPos, 1.0);
// calculate half-angle vector between view and light
m_viewVector = -vec3f(m_modelView.get_row(2));
if (dot(m_viewVector, m_lightVector) > 0) {
m_halfVector = normalize(m_viewVector + m_lightVector);
m_invertedView = false;
}
else {
m_halfVector = normalize(-m_viewVector + m_lightVector);
m_invertedView = true;
}
// calculate light view matrix
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
gluLookAt(m_lightPos[0],
m_lightPos[1],
m_lightPos[2],
m_lightTarget[0],
m_lightTarget[1],
m_lightTarget[2],
0.0,
1.0,
0.0);
// calculate light projection matrix
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPerspective(45.0, 1.0, 1.0, 200.0);
glGetFloatv(GL_MODELVIEW_MATRIX, (float *)m_lightView.get_value());
glGetFloatv(GL_PROJECTION_MATRIX, (float *)m_lightProj.get_value());
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
// construct shadow matrix
matrix4f scale;
scale.set_scale(vec3f(0.5, 0.5, 0.5));
matrix4f translate;
translate.set_translate(vec3f(0.5, 0.5, 0.5));
m_shadowMatrix = translate * scale * m_lightProj * m_lightView * inverse(m_modelView);
// calc object space eye position
m_eyePos = inverse(m_modelView) * vec4f(0.0, 0.0, 0.0, 1.0);
// calc half vector in eye space
m_halfVectorEye = m_modelView * vec4f(m_halfVector, 0.0);
}
// draw slice of particles from camera view
void SmokeRenderer::drawSlice(int i)
{
m_imageFbo->Bind();
glViewport(0, 0, m_imageW, m_imageH);
glColor4f(1.0, 1.0, 1.0, m_spriteAlpha);
if (m_invertedView) {
// front-to-back
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
}
else {
// back-to-front
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
drawPointSprites(m_particleShadowProg, i * m_batchSize, m_batchSize, true);
m_imageFbo->Disable();
}
// draw slice of particles from light's point of view
void SmokeRenderer::drawSliceLightView(int i)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf((GLfloat *)m_lightView.get_value());
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadMatrixf((GLfloat *)m_lightProj.get_value());
m_lightFbo->Bind();
glViewport(0, 0, m_lightBufferSize, m_lightBufferSize);
glColor4f(m_colorAttenuation[0] * m_shadowAlpha,
m_colorAttenuation[1] * m_shadowAlpha,
m_colorAttenuation[2] * m_shadowAlpha,
1.0);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
drawPointSprites(m_particleProg, i * m_batchSize, m_batchSize, false);
m_lightFbo->Disable();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
// draw particles as slices with shadowing
void SmokeRenderer::drawSlices()
{
m_batchSize = mNumParticles / m_numSlices;
// clear light buffer
m_srcLightTexture = 0;
m_lightFbo->Bind();
m_lightFbo->AttachTexture(GL_TEXTURE_2D, m_lightTexture[m_srcLightTexture], GL_COLOR_ATTACHMENT0_EXT);
glClearColor(1.0f - m_lightColor[0], 1.0f - m_lightColor[1], 1.0f - m_lightColor[2], 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
m_lightFbo->Disable();
// clear volume image
m_imageFbo->Bind();
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
m_imageFbo->Disable();
glActiveTexture(GL_TEXTURE0);
glMatrixMode(GL_TEXTURE);
glLoadMatrixf((GLfloat *)m_shadowMatrix.get_value());
// render slices
if (m_numDisplayedSlices > m_numSlices)
m_numDisplayedSlices = m_numSlices;
for (int i = 0; i < m_numDisplayedSlices; i++) {
// draw slice from camera view, sampling light buffer
drawSlice(i);
// draw slice from light view to light buffer, accumulating shadows
drawSliceLightView(i);
if (m_doBlur) {
blurLightBuffer();
}
}
glActiveTexture(GL_TEXTURE0);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
}
// blur light buffer to simulate scattering effects
void SmokeRenderer::blurLightBuffer()
{
m_lightFbo->Bind();
m_lightFbo->AttachTexture(GL_TEXTURE_2D, m_lightTexture[1 - m_srcLightTexture], GL_COLOR_ATTACHMENT0_EXT);
glViewport(0, 0, m_lightBufferSize, m_lightBufferSize);
m_blurProg->enable();
m_blurProg->bindTexture("tex", m_lightTexture[m_srcLightTexture], GL_TEXTURE_2D, 0);
m_blurProg->setUniform2f("texelSize", 1.0f / (float)m_lightBufferSize, 1.0f / (float)m_lightBufferSize);
m_blurProg->setUniform1f("blurRadius", m_blurRadius);
glDisable(GL_DEPTH_TEST);
drawQuad();
m_blurProg->disable();
m_srcLightTexture = 1 - m_srcLightTexture;
m_lightFbo->Disable();
}
// display texture to screen
void SmokeRenderer::displayTexture(GLuint tex)
{
m_displayTexProg->enable();
m_displayTexProg->bindTexture("tex", tex, GL_TEXTURE_2D, 0);
drawQuad();
m_displayTexProg->disable();
}
// composite final volume image on top of scene
void SmokeRenderer::compositeResult()
{
glViewport(0, 0, mWindowW, mWindowH);
glDisable(GL_DEPTH_TEST);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
displayTexture(m_imageTex);
glDisable(GL_BLEND);
}
void SmokeRenderer::render()
{
switch (mDisplayMode) {
case POINTS:
glColor3f(1.0, 1.0, 1.0);
m_simpleProg->enable();
drawPoints(0, mNumParticles, false);
m_simpleProg->disable();
break;
case SPRITES:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
glColor4f(1.0, 1.0, 1.0, m_spriteAlpha);
drawPointSprites(m_particleProg, 0, mNumParticles, false);
break;
case VOLUMETRIC:
drawSlices();
compositeResult();
break;
case NUM_MODES:
break;
}
if (m_displayLightBuffer) {
// display light buffer to screen
glViewport(0, 0, m_lightBufferSize, m_lightBufferSize);
glDisable(GL_DEPTH_TEST);
displayTexture(m_lightTexture[m_srcLightTexture]);
glViewport(0, 0, mWindowW, mWindowH);
}
glutReportErrors();
}
// render scene depth to texture
// (this is to ensure that particle are correctly occluded in the low-resolution
// render buffer)
void SmokeRenderer::beginSceneRender(Target target)
{
if (target == LIGHT_BUFFER) {
m_lightFbo->Bind();
glViewport(0, 0, m_lightBufferSize, m_lightBufferSize);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf((GLfloat *)m_lightView.get_value());
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadMatrixf((GLfloat *)m_lightProj.get_value());
}
else {
m_imageFbo->Bind();
glViewport(0, 0, m_imageW, m_imageH);
}
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_TRUE);
glClear(GL_DEPTH_BUFFER_BIT);
}
void SmokeRenderer::endSceneRender(Target target)
{
if (target == LIGHT_BUFFER) {
m_lightFbo->Disable();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
else {
m_imageFbo->Disable();
}
glViewport(0, 0, mWindowW, mWindowH);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
// create an OpenGL texture
GLuint SmokeRenderer::createTexture(GLenum target, int w, int h, GLint internalformat, GLenum format)
{
GLuint texid;
glGenTextures(1, &texid);
glBindTexture(target, texid);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(target, 0, internalformat, w, h, 0, format, GL_FLOAT, 0);
return texid;
}
// create buffers for off-screen rendering
void SmokeRenderer::createBuffers(int w, int h)
{
if (m_imageFbo) {
glDeleteTextures(1, &m_imageTex);
glDeleteTextures(1, &m_depthTex);
delete m_imageFbo;
}
mWindowW = w;
mWindowH = h;
m_imageW = w / m_downSample;
m_imageH = h / m_downSample;
// create fbo for image buffer
GLint format = GL_RGBA16F_ARB;
// GLint format = GL_LUMINANCE16F_ARB;
// GLint format = GL_RGBA8;
m_imageTex = createTexture(GL_TEXTURE_2D, m_imageW, m_imageH, format, GL_RGBA);
m_depthTex = createTexture(GL_TEXTURE_2D, m_imageW, m_imageH, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
m_imageFbo = new FramebufferObject();
m_imageFbo->AttachTexture(GL_TEXTURE_2D, m_imageTex, GL_COLOR_ATTACHMENT0_EXT);
m_imageFbo->AttachTexture(GL_TEXTURE_2D, m_depthTex, GL_DEPTH_ATTACHMENT_EXT);
m_imageFbo->IsValid();
}
void SmokeRenderer::setLightColor(vec3f c)
{
m_lightColor = c;
// set light texture border color
GLfloat borderColor[4] = {1.0f - m_lightColor[0], 1.0f - m_lightColor[1], 1.0f - m_lightColor[2], 0.0f};
glBindTexture(GL_TEXTURE_2D, m_lightTexture[0]);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glBindTexture(GL_TEXTURE_2D, m_lightTexture[1]);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glBindTexture(GL_TEXTURE_2D, 0);
}
// create FBOs for light buffer
void SmokeRenderer::createLightBuffer()
{
GLint format = GL_RGBA16F_ARB;
// GLint format = GL_RGBA8;
// GLint format = GL_LUMINANCE16F_ARB;
m_lightTexture[0] = createTexture(GL_TEXTURE_2D, m_lightBufferSize, m_lightBufferSize, format, GL_RGBA);
// make shadows clamp to light color at edges
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
m_lightTexture[1] = createTexture(GL_TEXTURE_2D, m_lightBufferSize, m_lightBufferSize, format, GL_RGBA);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
m_lightDepthTexture = createTexture(
GL_TEXTURE_2D, m_lightBufferSize, m_lightBufferSize, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT);
m_lightFbo = new FramebufferObject();
m_lightFbo->AttachTexture(GL_TEXTURE_2D, m_lightTexture[m_srcLightTexture], GL_COLOR_ATTACHMENT0_EXT);
m_lightFbo->AttachTexture(GL_TEXTURE_2D, m_lightDepthTexture, GL_DEPTH_ATTACHMENT_EXT);
m_lightFbo->IsValid();
}
void SmokeRenderer::setWindowSize(int w, int h)
{
mAspect = (float)mWindowW / (float)mWindowH;
mInvFocalLen = tan(mFov * 0.5f * NV_PI / 180.0f);
createBuffers(w, h);
}
void SmokeRenderer::drawQuad()
{
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(-1.0f, -1.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex2f(1.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex2f(1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex2f(-1.0f, 1.0f);
glEnd();
}
void SmokeRenderer::drawVector(vec3f v)
{
glBegin(GL_LINES);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3fv((float *)&v[0]);
glEnd();
}
// render vectors to screen for debugging
void SmokeRenderer::debugVectors()
{
glColor3f(1.0f, 1.0f, 0.0f);
drawVector(m_lightVector);
glColor3f(0.0f, 1.0f, 0.0f);
drawVector(m_viewVector);
glColor3f(0.0f, 0.0f, 1.0f);
drawVector(-m_viewVector);
glColor3f(1.0f, 0.0f, 0.0f);
drawVector(m_halfVector);
}

View File

@@ -0,0 +1,168 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Smoke particle renderer with volumetric shadows
#ifndef SMOKE_RENDERER_H
#define SMOKE_RENDERER_H
#include "GLSLProgram.h"
#include "framebufferObject.h"
#include "nvMath.h"
using namespace nv;
class SmokeRenderer
{
public:
SmokeRenderer(int maxParticles);
~SmokeRenderer();
enum DisplayMode { POINTS, SPRITES, VOLUMETRIC, NUM_MODES };
enum Target { LIGHT_BUFFER, SCENE_BUFFER };
void setDisplayMode(DisplayMode mode) { mDisplayMode = mode; }
void setNumParticles(unsigned int x) { mNumParticles = x; }
void setPositionBuffer(GLuint vbo) { mPosVbo = vbo; }
void setVelocityBuffer(GLuint vbo) { mVelVbo = vbo; }
void setColorBuffer(GLuint vbo) { mColorVbo = vbo; }
void setIndexBuffer(GLuint ib) { mIndexBuffer = ib; }
void setParticleRadius(float x) { mParticleRadius = x; }
void setWindowSize(int w, int h);
void setFOV(float fov) { mFov = fov; }
void setNumSlices(int x) { m_numSlices = x; }
void setNumDisplayedSlices(int x) { m_numDisplayedSlices = x; }
void setAlpha(float x) { m_spriteAlpha = x; }
void setShadowAlpha(float x) { m_shadowAlpha = x; }
void setColorAttenuation(vec3f c) { m_colorAttenuation = c; }
void setLightColor(vec3f c);
void setDoBlur(bool b) { m_doBlur = b; }
void setBlurRadius(float x) { m_blurRadius = x; }
void setDisplayLightBuffer(bool b) { m_displayLightBuffer = b; }
void beginSceneRender(Target target);
void endSceneRender(Target target);
void setLightPosition(vec3f v) { m_lightPos = v; }
void setLightTarget(vec3f v) { m_lightTarget = v; }
vec4f getLightPositionEyeSpace() { return m_lightPosEye; }
matrix4f getShadowMatrix() { return m_shadowMatrix; }
GLuint getShadowTexture() { return m_lightTexture[m_srcLightTexture]; }
void calcVectors();
vec3f getSortVector() { return m_halfVector; }
void render();
void debugVectors();
private:
void drawPoints(int start, int count, bool sort);
void drawPointSprites(GLSLProgram *prog, int start, int count, bool shadowed);
void drawSlice(int i);
void drawSliceLightView(int i);
void drawSlices();
void displayTexture(GLuint tex);
void compositeResult();
void blurLightBuffer();
void depthSort();
GLuint createTexture(GLenum target, int w, int h, GLint internalformat, GLenum format);
void createBuffers(int w, int h);
void createLightBuffer();
void drawQuad();
void drawVector(vec3f v);
// particle data
unsigned int mMaxParticles;
unsigned int mNumParticles;
GLuint mPosVbo;
GLuint mVelVbo;
GLuint mColorVbo;
GLuint mIndexBuffer;
float mParticleRadius;
DisplayMode mDisplayMode;
// window
unsigned int mWindowW, mWindowH;
float mAspect, mInvFocalLen;
float mFov;
int m_downSample;
int m_imageW, m_imageH;
int m_numSlices;
int m_numDisplayedSlices;
int m_batchSize;
int m_sliceNo;
float m_shadowAlpha;
float m_spriteAlpha;
bool m_doBlur;
float m_blurRadius;
bool m_displayLightBuffer;
vec3f m_lightVector, m_lightPos, m_lightTarget;
vec3f m_lightColor;
vec3f m_colorAttenuation;
float m_lightDistance;
matrix4f m_modelView, m_lightView, m_lightProj, m_shadowMatrix;
vec3f m_viewVector, m_halfVector;
bool m_invertedView;
vec4f m_eyePos;
vec4f m_halfVectorEye;
vec4f m_lightPosEye;
// programs
GLSLProgram *m_simpleProg;
GLSLProgram *m_particleProg, *m_particleShadowProg;
GLSLProgram *m_displayTexProg, *m_blurProg;
// image buffers
int m_lightBufferSize;
GLuint m_lightTexture[2];
int m_srcLightTexture;
GLuint m_lightDepthTexture;
FramebufferObject *m_lightFbo;
GLuint m_imageTex, m_depthTex;
FramebufferObject *m_imageFbo;
};
#endif

View File

@@ -0,0 +1,302 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// GLSL shaders for particle rendering
#define STRINGIFY(A) #A
// particle vertex shader
const char *particleVS = STRINGIFY(
uniform float pointRadius; // point size in world space \n
uniform float pointScale; // scale to calculate size in pixels \n
uniform vec4 eyePos; \n
void main() \n
{
\n
vec4 wpos = vec4(gl_Vertex.xyz, 1.0); \n
gl_Position = gl_ModelViewProjectionMatrix *wpos; \n
// calculate window-space point size \n
vec4 eyeSpacePos = gl_ModelViewMatrix *wpos; \n
float dist = length(eyeSpacePos.xyz); \n
gl_PointSize = pointRadius * (pointScale / dist); \n
gl_TexCoord[0] = gl_MultiTexCoord0; // sprite texcoord \n
gl_TexCoord[1] = eyeSpacePos; \n
gl_FrontColor = gl_Color; \n
} \n
);
// motion blur shaders
const char *mblurVS = STRINGIFY(uniform float timestep; \n void
main() \n {
\n vec3 pos = gl_Vertex.xyz;
\n vec3 vel = gl_MultiTexCoord0.xyz;
\n vec3 pos2 = (pos - vel * timestep).xyz; // previous position \n
gl_Position = gl_ModelViewMatrix * vec4(pos, 1.0);
\n // eye space
gl_TexCoord[0] = gl_ModelViewMatrix * vec4(pos2, 1.0);
\n
// aging \n
float lifetime = gl_MultiTexCoord0.w;
\n float age = gl_Vertex.w;
\n float phase = (lifetime > 0.0) ? (age / lifetime) : 1.0;
\n // [0, 1]
gl_TexCoord[1]
.x = phase;
\n float fade = 1.0 - phase;
\n
// float fade = 1.0; \n
// gl_FrontColor = gl_Color; \n
gl_FrontColor = vec4(gl_Color.xyz, gl_Color.w * fade);
\n
} \n);
// motion blur geometry shader
// - outputs stretched quad between previous and current positions
const char *mblurGS = "#version 120\n"
"#extension GL_EXT_geometry_shader4 : enable\n" STRINGIFY(
uniform float pointRadius; // point size in world space \n
void main() \n {
\n
// aging \n
float phase = gl_TexCoordIn[0][1].x;
\n float radius = pointRadius;
\n
// eye space \n
vec3 pos = gl_PositionIn[0].xyz;
\n vec3 pos2 = gl_TexCoordIn[0][0].xyz;
\n vec3 motion = pos - pos2;
\n vec3 dir = normalize(motion);
\n float len = length(motion);
\n
vec3 x = dir * radius;
\n vec3 view = normalize(-pos);
\n vec3 y = normalize(cross(dir, view)) * radius;
\n float facing = dot(view, dir);
\n
// check for very small motion to avoid jitter \n
float threshold = 0.01;
\n
if ((len < threshold) || (facing > 0.95) || (facing < -0.95))
{
\n pos2 = pos;
\n x = vec3(radius, 0.0, 0.0);
\n y = vec3(0.0, -radius, 0.0);
\n
}
\n
// output quad \n
gl_FrontColor = gl_FrontColorIn[0];
\n gl_TexCoord[0] = vec4(0, 0, 0, phase);
\n gl_TexCoord[1] = gl_PositionIn[0];
\n gl_Position = gl_ProjectionMatrix * vec4(pos + x + y, 1);
\n EmitVertex();
\n
gl_TexCoord[0] = vec4(0, 1, 0, phase);
\n gl_TexCoord[1] = gl_PositionIn[0];
\n gl_Position = gl_ProjectionMatrix * vec4(pos + x - y, 1);
\n EmitVertex();
\n
gl_TexCoord[0] = vec4(1, 0, 0, phase);
\n gl_TexCoord[1] = gl_PositionIn[0];
\n gl_Position = gl_ProjectionMatrix * vec4(pos2 - x + y, 1);
\n EmitVertex();
\n
gl_TexCoord[0] = vec4(1, 1, 0, phase);
\n gl_TexCoord[1] = gl_PositionIn[0];
\n gl_Position = gl_ProjectionMatrix * vec4(pos2 - x - y, 1);
\n EmitVertex();
\n
} \n);
const char *simplePS = STRINGIFY(void main() \n {
\n gl_FragColor = gl_Color;
\n
} \n);
// render particle without shadows
const char *particlePS = STRINGIFY(uniform float pointRadius; \n void
main() \n {
\n
// calculate eye-space sphere normal from texture coordinates \n
vec3 N;
\n N.xy = gl_TexCoord[0].xy * vec2(2.0, -2.0) + vec2(-1.0, 1.0);
\n float r2 = dot(N.xy, N.xy);
\n
if (r2 > 1.0) discard; // kill pixels outside circle \n
N.z = sqrt(1.0 - r2);
\n
// float alpha = saturate(1.0 - r2); \n
float alpha = clamp((1.0 - r2), 0.0, 1.0);
\n alpha *= gl_Color.w;
\n
gl_FragColor = vec4(gl_Color.xyz * alpha, alpha);
\n
} \n);
// render particle including shadows
const char *particleShadowPS = STRINGIFY(
uniform float pointRadius; \n uniform sampler2D shadowTex; \n uniform sampler2D depthTex; \n void
main() \n {
\n
// calculate eye-space sphere normal from texture coordinates \n
vec3 N;
\n N.xy = gl_TexCoord[0].xy * vec2(2.0, -2.0) + vec2(-1.0, 1.0);
\n float r2 = dot(N.xy, N.xy);
\n
if (r2 > 1.0) discard;
\n // kill pixels outside circle
N.z = sqrt(1.0 - r2);
\n vec4 eyeSpacePos = gl_TexCoord[1];
\n vec4 eyeSpaceSpherePos = vec4(eyeSpacePos.xyz + N * pointRadius, 1.0);
\n // point on sphere
vec4 shadowPos = gl_TextureMatrix[0] * eyeSpaceSpherePos;
\n vec3 shadow = vec3(1.0) - texture2DProj(shadowTex, shadowPos.xyw).xyz;
\n
// float alpha = saturate(1.0 - r2); \n
float alpha = clamp((1.0 - r2), 0.0, 1.0);
\n alpha *= gl_Color.w;
\n
gl_FragColor = vec4(gl_Color.xyz * shadow * alpha, alpha);
\n // premul alpha
});
// render particle as lit sphere
const char *particleSpherePS = STRINGIFY(
uniform float pointRadius; \n uniform vec3 lightDir = vec3(0.577, 0.577, 0.577); \n void
main() \n {
\n
// calculate eye-space sphere normal from texture coordinates \n
vec3 N;
\n N.xy = gl_TexCoord[0].xy * vec2(2.0, -2.0) + vec2(-1.0, 1.0);
\n float r2 = dot(N.xy, N.xy);
\n
if (r2 > 1.0) discard; // kill pixels outside circle \n
N.z = sqrt(1.0 - r2);
\n
// calculate depth \n
vec4 eyeSpacePos =
vec4(gl_TexCoord[1].xyz + N * pointRadius, 1.0); // position of this pixel on sphere in eye space \n
vec4 clipSpacePos = gl_ProjectionMatrix * eyeSpacePos;
\n gl_FragDepth = (clipSpacePos.z / clipSpacePos.w) * 0.5 + 0.5;
\n
float diffuse = max(0.0, dot(N, lightDir));
\n
gl_FragColor = diffuse * gl_Color;
\n
} \n);
const char *passThruVS = STRINGIFY(void main() \n {
\n gl_Position = gl_Vertex;
\n gl_TexCoord[0] = gl_MultiTexCoord0;
\n gl_FrontColor = gl_Color;
\n
} \n);
const char *texture2DPS = STRINGIFY(uniform sampler2D tex; \n void
main() \n {
\n gl_FragColor = texture2D(tex, gl_TexCoord[0].xy);
\n
} \n);
// 4 tap 3x3 gaussian blur
const char *blurPS = STRINGIFY(
uniform sampler2D tex; \n uniform vec2 texelSize; \n uniform float blurRadius; \n void
main() \n {
\n vec4 c;
\n c = texture2D(tex, gl_TexCoord[0].xy + vec2(-0.5, -0.5) * texelSize * blurRadius);
\n c += texture2D(tex, gl_TexCoord[0].xy + vec2(0.5, -0.5) * texelSize * blurRadius);
\n c += texture2D(tex, gl_TexCoord[0].xy + vec2(0.5, 0.5) * texelSize * blurRadius);
\n c += texture2D(tex, gl_TexCoord[0].xy + vec2(-0.5, 0.5) * texelSize * blurRadius);
\n c *= 0.25;
\n
gl_FragColor = c;
\n
} \n);
// floor shader
const char *floorVS = STRINGIFY(
varying vec4 vertexPosEye; // vertex position in eye space \n
varying vec3 normalEye; \n
void main() \n
{
\n
gl_Position = gl_ModelViewProjectionMatrix *gl_Vertex; \n
gl_TexCoord[0] = gl_MultiTexCoord0; \n
vertexPosEye = gl_ModelViewMatrix *gl_Vertex; \n
normalEye = gl_NormalMatrix *gl_Normal; \n
gl_FrontColor = gl_Color; \n
} \n
);
const char *floorPS = STRINGIFY(
uniform vec3 lightPosEye; // light position in eye space \n
uniform vec3 lightColor; \n
uniform sampler2D tex; \n
uniform sampler2D shadowTex; \n
varying vec4 vertexPosEye; // vertex position in eye space \n
varying vec3 normalEye; \n
void main() \n
{
\n
vec4 shadowPos = gl_TextureMatrix[0] * vertexPosEye; \n
vec4 colorMap = texture2D(tex, gl_TexCoord[0].xy); \n
vec3 N = normalize(normalEye); \n
vec3 L = normalize(lightPosEye - vertexPosEye.xyz); \n
float diffuse = max(0.0, dot(N, L)); \n
vec3 shadow = vec3(1.0) - texture2DProj(shadowTex, shadowPos.xyw).xyz; \n
if (shadowPos.w < 0.0) shadow = lightColor; \n // avoid back projections
gl_FragColor = vec4(gl_Color.xyz *colorMap.xyz *diffuse * shadow, 1.0); \n
} \n
);

View File

@@ -0,0 +1,32 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
extern const char *particleVS;
extern const char *particleSpherePS, *simplePS, *particlePS, *particleShadowPS;
extern const char *mblurVS, *mblurGS;
extern const char *passThruVS, *texture2DPS, *blurPS;
extern const char *floorVS, *floorPS;

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,366 @@
/*
Copyright (c) 2005,
Aaron Lefohn (lefohn@cs.ucdavis.edu)
Robert Strzodka (strzodka@stanford.edu)
Adam Moerschell (atmoerschell@ucdavis.edu)
All rights reserved.
This software is licensed under the BSD open-source license. See
http://www.opensource.org/licenses/bsd-license.php for more detail.
*************************************************************
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of the University of California, Davis nor the names of
the contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define HELPERGL_EXTERN_GL_FUNC_IMPLEMENTATION
#include "framebufferObject.h"
#include <helper_gl.h>
#include <iostream>
using namespace std;
FramebufferObject::FramebufferObject()
: m_fboId(_GenerateFboId())
, m_savedFboId(0)
{
// Bind this FBO so that it actually gets created now
_GuardedBind();
_GuardedUnbind();
}
FramebufferObject::~FramebufferObject() { glDeleteFramebuffersEXT(1, &m_fboId); }
void FramebufferObject::Bind() { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fboId); }
void FramebufferObject::Disable() { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); }
void FramebufferObject::AttachTexture(GLenum texTarget, GLuint texId, GLenum attachment, int mipLevel, int zSlice)
{
_GuardedBind();
/*
#ifndef NDEBUG
if( GetAttachedId(attachment) != texId ) {
#endif
*/
_FramebufferTextureND(attachment, texTarget, texId, mipLevel, zSlice);
/*
#ifndef NDEBUG
}
else {
cerr << "FramebufferObject::AttachTexture PERFORMANCE WARNING:\n"
<< "\tRedundant bind of texture (id = " << texId << ").\n"
<< "\tHINT : Compile with -DNDEBUG to remove this warning.\n";
}
#endif
*/
_GuardedUnbind();
}
void FramebufferObject::AttachTextures(int numTextures,
GLenum texTarget[],
GLuint texId[],
GLenum attachment[],
int mipLevel[],
int zSlice[])
{
for (int i = 0; i < numTextures; ++i) {
AttachTexture(texTarget[i],
texId[i],
attachment ? attachment[i] : (GL_COLOR_ATTACHMENT0_EXT + i),
mipLevel ? mipLevel[i] : 0,
zSlice ? zSlice[i] : 0);
}
}
void FramebufferObject::AttachRenderBuffer(GLuint buffId, GLenum attachment)
{
_GuardedBind();
#ifndef NDEBUG
if (GetAttachedId(attachment) != buffId) {
#endif
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, GL_RENDERBUFFER_EXT, buffId);
#ifndef NDEBUG
}
else {
cerr << "FramebufferObject::AttachRenderBuffer PERFORMANCE WARNING:\n"
<< "\tRedundant bind of Renderbuffer (id = " << buffId << ")\n"
<< "\tHINT : Compile with -DNDEBUG to remove this warning.\n";
}
#endif
_GuardedUnbind();
}
void FramebufferObject::AttachRenderBuffers(int numBuffers, GLuint buffId[], GLenum attachment[])
{
for (int i = 0; i < numBuffers; ++i) {
AttachRenderBuffer(buffId[i], attachment ? attachment[i] : (GL_COLOR_ATTACHMENT0_EXT + i));
}
}
void FramebufferObject::Unattach(GLenum attachment)
{
_GuardedBind();
GLenum type = GetAttachedType(attachment);
switch (type) {
case GL_NONE:
break;
case GL_RENDERBUFFER_EXT:
AttachRenderBuffer(0, attachment);
break;
case GL_TEXTURE:
AttachTexture(GL_TEXTURE_2D, 0, attachment);
break;
default:
cerr << "FramebufferObject::unbind_attachment ERROR: Unknown attached "
"resource type\n";
}
_GuardedUnbind();
}
void FramebufferObject::UnattachAll()
{
int numAttachments = GetMaxColorAttachments();
for (int i = 0; i < numAttachments; ++i) {
Unattach(GL_COLOR_ATTACHMENT0_EXT + i);
}
}
int FramebufferObject::GetMaxColorAttachments()
{
GLint maxAttach = 0;
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &maxAttach);
return maxAttach;
}
GLuint FramebufferObject::_GenerateFboId()
{
GLuint id = 0;
glGenFramebuffersEXT(1, &id);
return id;
}
void FramebufferObject::_GuardedBind()
{
// Only binds if m_fboId is different than the currently bound FBO
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &m_savedFboId);
if (m_fboId != (GLuint)m_savedFboId) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fboId);
}
}
void FramebufferObject::_GuardedUnbind()
{
// Returns FBO binding to the previously enabled FBO
if (m_fboId != (GLuint)m_savedFboId) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint)m_savedFboId);
}
}
void FramebufferObject::_FramebufferTextureND(GLenum attachment,
GLenum texTarget,
GLuint texId,
int mipLevel,
int zSlice)
{
if (texTarget == GL_TEXTURE_1D) {
glFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, attachment, GL_TEXTURE_1D, texId, mipLevel);
}
else if (texTarget == GL_TEXTURE_3D) {
glFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, attachment, GL_TEXTURE_3D, texId, mipLevel, zSlice);
}
else {
// Default is GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_ARB, or cube faces
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, texTarget, texId, mipLevel);
}
}
#ifndef NDEBUG
bool FramebufferObject::IsValid(ostream &ostr)
{
_GuardedBind();
bool isOK = false;
GLenum status;
status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
switch (status) {
case GL_FRAMEBUFFER_COMPLETE_EXT: // Everything's OK
isOK = true;
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
ostr << "glift::CheckFramebufferStatus() ERROR:\n\t"
<< "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT\n";
isOK = false;
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
ostr << "glift::CheckFramebufferStatus() ERROR:\n\t"
<< "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT\n";
isOK = false;
break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
ostr << "glift::CheckFramebufferStatus() ERROR:\n\t"
<< "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT\n";
isOK = false;
break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
ostr << "glift::CheckFramebufferStatus() ERROR:\n\t"
<< "GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT\n";
isOK = false;
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
ostr << "glift::CheckFramebufferStatus() ERROR:\n\t"
<< "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT\n";
isOK = false;
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
ostr << "glift::CheckFramebufferStatus() ERROR:\n\t"
<< "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT\n";
isOK = false;
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
ostr << "glift::CheckFramebufferStatus() ERROR:\n\t"
<< "GL_FRAMEBUFFER_UNSUPPORTED_EXT\n";
isOK = false;
break;
default:
ostr << "glift::CheckFramebufferStatus() ERROR:\n\t"
<< "Unknown ERROR\n";
isOK = false;
}
_GuardedUnbind();
return isOK;
}
#endif // NDEBUG
/// Accessors
GLenum FramebufferObject::GetAttachedType(GLenum attachment)
{
// Returns GL_RENDERBUFFER_EXT or GL_TEXTURE
_GuardedBind();
GLint type = 0;
glGetFramebufferAttachmentParameterivEXT(
GL_FRAMEBUFFER_EXT, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT, &type);
_GuardedUnbind();
return GLenum(type);
}
GLuint FramebufferObject::GetAttachedId(GLenum attachment)
{
_GuardedBind();
GLint id = 0;
glGetFramebufferAttachmentParameterivEXT(
GL_FRAMEBUFFER_EXT, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &id);
_GuardedUnbind();
return GLuint(id);
}
GLint FramebufferObject::GetAttachedMipLevel(GLenum attachment)
{
_GuardedBind();
GLint level = 0;
glGetFramebufferAttachmentParameterivEXT(
GL_FRAMEBUFFER_EXT, attachment, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT, &level);
_GuardedUnbind();
return level;
}
GLint FramebufferObject::GetAttachedCubeFace(GLenum attachment)
{
_GuardedBind();
GLint level = 0;
glGetFramebufferAttachmentParameterivEXT(
GL_FRAMEBUFFER_EXT, attachment, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT, &level);
_GuardedUnbind();
return level;
}
GLint FramebufferObject::GetAttachedZSlice(GLenum attachment)
{
_GuardedBind();
GLint slice = 0;
glGetFramebufferAttachmentParameterivEXT(
GL_FRAMEBUFFER_EXT, attachment, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT, &slice);
_GuardedUnbind();
return slice;
}

View File

@@ -0,0 +1,230 @@
/*
Copyright (c) 2005,
Aaron Lefohn (lefohn@cs.ucdavis.edu)
Robert Strzodka (strzodka@stanford.edu)
Adam Moerschell (atmoerschell@ucdavis.edu)
All rights reserved.
This software is licensed under the BSD open-source license. See
http://www.opensource.org/licenses/bsd-license.php for more detail.
*************************************************************
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of the University of California, Davis nor the names of
the contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef UCDAVIS_FRAMEBUFFER_OBJECT_H
#define UCDAVIS_FRAMEBUFFER_OBJECT_H
#include <helper_gl.h>
#include <iostream>
/*!
FramebufferObject Class. This class encapsulates the FramebufferObject
(FBO) OpenGL spec. See the official spec at:
http://oss.sgi.com/projects/ogl-sample/registry/EXT/framebuffer_object.txt
for details.
A framebuffer object (FBO) is conceptually a structure containing pointers
to GPU memory. The memory pointed to is either an OpenGL texture or an
OpenGL RenderBuffer. FBOs can be used to render to one or more textures,
share depth buffers between multiple sets of color buffers/textures and
are a complete replacement for pbuffers.
Performance Notes:
1) It is more efficient (but not required) to call Bind()
on an FBO before making multiple method calls. For example:
FramebufferObject fbo;
fbo.Bind();
fbo.AttachTexture(GL_TEXTURE_2D, texId0, GL_COLOR_ATTACHMENT0_EXT);
fbo.AttachTexture(GL_TEXTURE_2D, texId1, GL_COLOR_ATTACHMENT1_EXT);
fbo.IsValid();
To provide a complete encapsulation, the following usage
pattern works correctly but is less efficient:
FramebufferObject fbo;
// NOTE : No Bind() call
fbo.AttachTexture(GL_TEXTURE_2D, texId0, GL_COLOR_ATTACHMENT0_EXT);
fbo.AttachTexture(GL_TEXTURE_2D, texId1, GL_COLOR_ATTACHMENT1_EXT);
fbo.IsValid();
The first usage pattern binds the FBO only once, whereas
the second usage binds/unbinds the FBO for each method call.
2) Use FramebufferObject::Disable() sparingly. We have intentionally
left out an "Unbind()" method because it is largely unnecessary
and encourages rendundant Bind/Unbind coding. Binding an FBO is
usually much faster than enabling/disabling a pbuffer, but is
still a costly operation. When switching between multiple FBOs
and a visible OpenGL framebuffer, the following usage pattern
is recommended:
FramebufferObject fbo1, fbo2;
fbo1.Bind();
... Render ...
// NOTE : No Unbind/Disable here...
fbo2.Bind();
... Render ...
// Disable FBO rendering and return to visible window
// OpenGL framebuffer.
FramebufferObject::Disable();
*/
class FramebufferObject
{
public:
/// Ctor/Dtor
FramebufferObject();
virtual ~FramebufferObject();
/// Bind this FBO as current render target
void Bind();
/// Bind a texture to the "attachment" point of this FBO
virtual void AttachTexture(GLenum texTarget,
GLuint texId,
GLenum attachment = GL_COLOR_ATTACHMENT0_EXT,
int mipLevel = 0,
int zSlice = 0);
/// Bind an array of textures to multiple "attachment" points of this FBO
/// - By default, the first 'numTextures' attachments are used,
/// starting with GL_COLOR_ATTACHMENT0_EXT
virtual void AttachTextures(int numTextures,
GLenum texTarget[],
GLuint texId[],
GLenum attachment[] = NULL,
int mipLevel[] = NULL,
int zSlice[] = NULL);
/// Bind a render buffer to the "attachment" point of this FBO
virtual void AttachRenderBuffer(GLuint buffId, GLenum attachment = GL_COLOR_ATTACHMENT0_EXT);
/// Bind an array of render buffers to corresponding "attachment" points
/// of this FBO.
/// - By default, the first 'numBuffers' attachments are used,
/// starting with GL_COLOR_ATTACHMENT0_EXT
virtual void AttachRenderBuffers(int numBuffers, GLuint buffId[], GLenum attachment[] = NULL);
/// Free any resource bound to the "attachment" point of this FBO
void Unattach(GLenum attachment);
/// Free any resources bound to any attachment points of this FBO
void UnattachAll();
/// Is this FBO currently a valid render target?
/// - Sends output to std::cerr by default but can
/// be a user-defined C++ stream
///
/// NOTE : This function works correctly in debug build
/// mode but always returns "true" if NDEBUG is
/// is defined (optimized builds)
#ifndef NDEBUG
bool IsValid(std::ostream &ostr = std::cerr);
#else
bool IsValid(std::ostream &ostr = std::cerr) { return true; }
#endif
/// BEGIN : Accessors
/// Is attached type GL_RENDERBUFFER_EXT or GL_TEXTURE?
GLenum GetAttachedType(GLenum attachment);
/// What is the Id of Renderbuffer/texture currently
/// attached to "attachment?"
GLuint GetAttachedId(GLenum attachment);
/// Which mipmap level is currently attached to "attachment?"
GLint GetAttachedMipLevel(GLenum attachment);
/// Which cube face is currently attached to "attachment?"
GLint GetAttachedCubeFace(GLenum attachment);
/// Which z-slice is currently attached to "attachment?"
GLint GetAttachedZSlice(GLenum attachment);
/// END : Accessors
/// BEGIN : Static methods global to all FBOs
/// Return number of color attachments permitted
static int GetMaxColorAttachments();
/// Disable all FBO rendering and return to traditional,
/// windowing-system controlled framebuffer
/// NOTE:
/// This is NOT an "unbind" for this specific FBO, but rather
/// disables all FBO rendering. This call is intentionally "static"
/// and named "Disable" instead of "Unbind" for this reason. The
/// motivation for this strange semantic is performance. Providing
/// "Unbind" would likely lead to a large number of unnecessary
/// FBO enabling/disabling.
static void Disable();
/// END : Static methods global to all FBOs
protected:
void _GuardedBind();
void _GuardedUnbind();
void _FramebufferTextureND(GLenum attachment, GLenum texTarget, GLuint texId, int mipLevel, int zSlice);
static GLuint _GenerateFboId();
private:
GLuint m_fboId;
GLint m_savedFboId;
};
#endif

View File

@@ -0,0 +1,89 @@
/*
* Copyright 1993-2015 NVIDIA Corporation. All rights reserved.
*
* Please refer to the NVIDIA end user license agreement (EULA) associated
* with this source code for terms and conditions that govern your use of
* this software. Any use, reproduction, disclosure, or distribution of
* this software and related documentation outside the terms of the EULA
* is strictly prohibited.
*
*/
//
// Template math library for common 3D functionality
//
// This code is in part deriver from glh, a cross platform glut helper library.
// The copyright for glh follows this notice.
//
// Copyright (c) NVIDIA Corporation. All rights reserved.
////////////////////////////////////////////////////////////////////////////////
/*
Copyright (c) 2000 Cass Everitt
Copyright (c) 2000 NVIDIA Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
* The names of contributors to this software may not be used
to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Cass Everitt - cass@r3.nu
*/
#ifndef NV_MATH_H
#define NV_MATH_H
#include <math.h>
#include <nvMatrix.h>
#include <nvQuaternion.h>
#include <nvVector.h>
#define NV_PI float(3.1415926535897932384626433832795)
namespace nv {
typedef vec2<float> vec2f;
typedef vec3<float> vec3f;
typedef vec3<int> vec3i;
typedef vec3<unsigned int> vec3ui;
typedef vec4<float> vec4f;
typedef matrix4<float> matrix4f;
typedef quaternion<float> quaternionf;
inline void applyRotation(const quaternionf &r)
{
float angle;
vec3f axis;
r.get_value(axis, angle);
glRotatef(angle / 3.1415926f * 180.0f, axis[0], axis[1], axis[2]);
}
}; // namespace nv
#endif

View File

@@ -0,0 +1,482 @@
/*
* Copyright 1993-2015 NVIDIA Corporation. All rights reserved.
*
* Please refer to the NVIDIA end user license agreement (EULA) associated
* with this source code for terms and conditions that govern your use of
* this software. Any use, reproduction, disclosure, or distribution of
* this software and related documentation outside the terms of the EULA
* is strictly prohibited.
*
*/
//
// Template math library for common 3D functionality
//
// nvMatrix.h - template matrix code
//
// This code is in part deriver from glh, a cross platform glut helper library.
// The copyright for glh follows this notice.
//
// Copyright (c) NVIDIA Corporation. All rights reserved.
////////////////////////////////////////////////////////////////////////////////
/*
Copyright (c) 2000 Cass Everitt
Copyright (c) 2000 NVIDIA Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
* The names of contributors to this software may not be used
to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Cass Everitt - cass@r3.nu
*/
#ifndef NV_MATRIX_H
#define NV_MATRIX_H
namespace nv {
template <class T> class vec2;
template <class T> class vec3;
template <class T> class vec4;
////////////////////////////////////////////////////////////////////////////////
//
// Matrix
//
////////////////////////////////////////////////////////////////////////////////
template <class T> class matrix4
{
public:
matrix4() { make_identity(); }
matrix4(T t) { set_value(t); }
matrix4(const T *m) { set_value(m); }
matrix4(T a00,
T a01,
T a02,
T a03,
T a10,
T a11,
T a12,
T a13,
T a20,
T a21,
T a22,
T a23,
T a30,
T a31,
T a32,
T a33)
: _11(a00)
, _12(a01)
, _13(a02)
, _14(a03)
, _21(a10)
, _22(a11)
, _23(a12)
, _24(a13)
, _31(a20)
, _32(a21)
, _33(a22)
, _34(a23)
, _41(a30)
, _42(a31)
, _43(a32)
, _44(a33)
{
}
void get_value(T *mp) const
{
int c = 0;
for (int j = 0; j < 4; j++)
for (int i = 0; i < 4; i++) {
mp[c++] = element(i, j);
}
}
const T *get_value() const { return _array; }
void set_value(T *mp)
{
int c = 0;
for (int j = 0; j < 4; j++)
for (int i = 0; i < 4; i++) {
element(i, j) = mp[c++];
}
}
void set_value(T r)
{
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++) {
element(i, j) = r;
}
}
void make_identity()
{
element(0, 0) = 1.0;
element(0, 1) = 0.0;
element(0, 2) = 0.0;
element(0, 3) = 0.0;
element(1, 0) = 0.0;
element(1, 1) = 1.0;
element(1, 2) = 0.0;
element(1, 3) = 0.0;
element(2, 0) = 0.0;
element(2, 1) = 0.0;
element(2, 2) = 1.0;
element(2, 3) = 0.0;
element(3, 0) = 0.0;
element(3, 1) = 0.0;
element(3, 2) = 0.0;
element(3, 3) = 1.0;
}
// set a uniform scale
void set_scale(T s)
{
element(0, 0) = s;
element(1, 1) = s;
element(2, 2) = s;
}
void set_scale(const vec3<T> &s)
{
for (int i = 0; i < 3; i++) {
element(i, i) = s[i];
}
}
void set_translate(const vec3<T> &t)
{
for (int i = 0; i < 3; i++) {
element(i, 3) = t[i];
}
}
void set_row(int r, const vec4<T> &t)
{
for (int i = 0; i < 4; i++) {
element(r, i) = t[i];
}
}
void set_column(int c, const vec4<T> &t)
{
for (int i = 0; i < 4; i++) {
element(i, c) = t[i];
}
}
vec4<T> get_row(int r) const
{
vec4<T> v;
for (int i = 0; i < 4; i++) {
v[i] = element(r, i);
}
return v;
}
vec4<T> get_column(int c) const
{
vec4<T> v;
for (int i = 0; i < 4; i++) {
v[i] = element(i, c);
}
return v;
}
friend matrix4 inverse(const matrix4 &m)
{
matrix4 minv;
T r1[8], r2[8], r3[8], r4[8];
T *s[4], *tmprow;
s[0] = &r1[0];
s[1] = &r2[0];
s[2] = &r3[0];
s[3] = &r4[0];
register int i, j, p, jj;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
s[i][j] = m.element(i, j);
if (i == j) {
s[i][j + 4] = 1.0;
}
else {
s[i][j + 4] = 0.0;
}
}
}
T scp[4];
for (i = 0; i < 4; i++) {
scp[i] = T(fabs(s[i][0]));
for (j = 1; j < 4; j++)
if (T(fabs(s[i][j])) > scp[i]) {
scp[i] = T(fabs(s[i][j]));
}
if (scp[i] == 0.0) {
return minv; // singular matrix!
}
}
int pivot_to;
T scp_max;
for (i = 0; i < 4; i++) {
// select pivot row
pivot_to = i;
scp_max = T(fabs(s[i][i] / scp[i]));
// find out which row should be on top
for (p = i + 1; p < 4; p++)
if (T(fabs(s[p][i] / scp[p])) > scp_max) {
scp_max = T(fabs(s[p][i] / scp[p]));
pivot_to = p;
}
// Pivot if necessary
if (pivot_to != i) {
tmprow = s[i];
s[i] = s[pivot_to];
s[pivot_to] = tmprow;
T tmpscp;
tmpscp = scp[i];
scp[i] = scp[pivot_to];
scp[pivot_to] = tmpscp;
}
T mji;
// perform gaussian elimination
for (j = i + 1; j < 4; j++) {
mji = s[j][i] / s[i][i];
s[j][i] = 0.0;
for (jj = i + 1; jj < 8; jj++) {
s[j][jj] -= mji * s[i][jj];
}
}
}
if (s[3][3] == 0.0) {
return minv; // singular matrix!
}
//
// Now we have an upper triangular matrix.
//
// x x x x | y y y y
// 0 x x x | y y y y
// 0 0 x x | y y y y
// 0 0 0 x | y y y y
//
// we'll back substitute to get the inverse
//
// 1 0 0 0 | z z z z
// 0 1 0 0 | z z z z
// 0 0 1 0 | z z z z
// 0 0 0 1 | z z z z
//
T mij;
for (i = 3; i > 0; i--) {
for (j = i - 1; j > -1; j--) {
mij = s[j][i] / s[i][i];
for (jj = j + 1; jj < 8; jj++) {
s[j][jj] -= mij * s[i][jj];
}
}
}
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++) {
minv(i, j) = s[i][j + 4] / s[i][i];
}
return minv;
}
friend matrix4 transpose(const matrix4 &m)
{
matrix4 mtrans;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++) {
mtrans(i, j) = m.element(j, i);
}
return mtrans;
}
matrix4 &operator*=(const matrix4 &rhs)
{
matrix4 mt(*this);
set_value(T(0));
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
for (int c = 0; c < 4; c++) {
element(i, j) += mt(i, c) * rhs(c, j);
}
return *this;
}
friend matrix4 operator*(const matrix4 &lhs, const matrix4 &rhs)
{
matrix4 r(T(0));
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
for (int c = 0; c < 4; c++) {
r.element(i, j) += lhs(i, c) * rhs(c, j);
}
return r;
}
// dst = M * src
vec4<T> operator*(const vec4<T> &src) const
{
vec4<T> r;
for (int i = 0; i < 4; i++)
r[i] = (src[0] * element(i, 0) + src[1] * element(i, 1) + src[2] * element(i, 2) + src[3] * element(i, 3));
return r;
}
// dst = src * M
friend vec4<T> operator*(const vec4<T> &lhs, const matrix4 &rhs)
{
vec4<T> r;
for (int i = 0; i < 4; i++)
r[i] = (lhs[0] * rhs.element(0, i) + lhs[1] * rhs.element(1, i) + lhs[2] * rhs.element(2, i)
+ lhs[3] * rhs.element(3, i));
return r;
}
T &operator()(int row, int col) { return element(row, col); }
const T &operator()(int row, int col) const { return element(row, col); }
T &element(int row, int col) { return _array[row | (col << 2)]; }
const T &element(int row, int col) const { return _array[row | (col << 2)]; }
matrix4 &operator*=(const T &r)
{
for (int i = 0; i < 4; ++i) {
element(0, i) *= r;
element(1, i) *= r;
element(2, i) *= r;
element(3, i) *= r;
}
return *this;
}
matrix4 &operator+=(const matrix4 &mat)
{
for (int i = 0; i < 4; ++i) {
element(0, i) += mat.element(0, i);
element(1, i) += mat.element(1, i);
element(2, i) += mat.element(2, i);
element(3, i) += mat.element(3, i);
}
return *this;
}
friend bool operator==(const matrix4 &lhs, const matrix4 &rhs)
{
bool r = true;
for (int i = 0; i < 16; i++) {
r &= lhs._array[i] == rhs._array[i];
}
return r;
}
friend bool operator!=(const matrix4 &lhs, const matrix4 &rhs)
{
bool r = true;
for (int i = 0; i < 16; i++) {
r &= lhs._array[i] != rhs._array[i];
}
return r;
}
union
{
struct
{
T _11, _12, _13, _14; // standard names for components
T _21, _22, _23, _24; // standard names for components
T _31, _32, _33, _34; // standard names for components
T _41, _42, _43, _44; // standard names for components
};
T _array[16]; // array access
};
};
}; // namespace nv
#endif

View File

@@ -0,0 +1,455 @@
/*
* Copyright 1993-2015 NVIDIA Corporation. All rights reserved.
*
* Please refer to the NVIDIA end user license agreement (EULA) associated
* with this source code for terms and conditions that govern your use of
* this software. Any use, reproduction, disclosure, or distribution of
* this software and related documentation outside the terms of the EULA
* is strictly prohibited.
*
*/
//
// Template math library for common 3D functionality
//
// nvQuaterion.h - quaternion template and utility functions
//
// This code is in part deriver from glh, a cross platform glut helper library.
// The copyright for glh follows this notice.
//
// Copyright (c) NVIDIA Corporation. All rights reserved.
////////////////////////////////////////////////////////////////////////////////
/*
Copyright (c) 2000 Cass Everitt
Copyright (c) 2000 NVIDIA Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
* The names of contributors to this software may not be used
to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Cass Everitt - cass@r3.nu
*/
#ifndef NV_QUATERNION_H
#define NV_QUATERNION_H
namespace nv {
template <class T> class vec2;
template <class T> class vec3;
template <class T> class vec4;
////////////////////////////////////////////////////////////////////////////////
//
// Quaternion
//
////////////////////////////////////////////////////////////////////////////////
template <class T> class quaternion
{
public:
quaternion()
: x(0.0)
, y(0.0)
, z(0.0)
, w(0.0)
{
}
quaternion(const T v[4]) { set_value(v); }
quaternion(T q0, T q1, T q2, T q3) { set_value(q0, q1, q2, q3); }
quaternion(const matrix4<T> &m) { set_value(m); }
quaternion(const vec3<T> &axis, T radians) { set_value(axis, radians); }
quaternion(const vec3<T> &rotateFrom, const vec3<T> &rotateTo) { set_value(rotateFrom, rotateTo); }
quaternion(const vec3<T> &from_look, const vec3<T> &from_up, const vec3<T> &to_look, const vec3<T> &to_up)
{
set_value(from_look, from_up, to_look, to_up);
}
const T *get_value() const { return &_array[0]; }
void get_value(T &q0, T &q1, T &q2, T &q3) const
{
q0 = _array[0];
q1 = _array[1];
q2 = _array[2];
q3 = _array[3];
}
quaternion &set_value(T q0, T q1, T q2, T q3)
{
_array[0] = q0;
_array[1] = q1;
_array[2] = q2;
_array[3] = q3;
return *this;
}
void get_value(vec3<T> &axis, T &radians) const
{
radians = T(acos(_array[3]) * T(2.0));
if (radians == T(0.0)) {
axis = vec3<T>(0.0, 0.0, 1.0);
}
else {
axis[0] = _array[0];
axis[1] = _array[1];
axis[2] = _array[2];
axis = normalize(axis);
}
}
void get_value(matrix4<T> &m) const
{
T s, xs, ys, zs, wx, wy, wz, xx, xy, xz, yy, yz, zz;
T norm = _array[0] * _array[0] + _array[1] * _array[1] + _array[2] * _array[2] + _array[3] * _array[3];
s = (norm == T(0.0)) ? T(0.0) : (T(2.0) / norm);
xs = _array[0] * s;
ys = _array[1] * s;
zs = _array[2] * s;
wx = _array[3] * xs;
wy = _array[3] * ys;
wz = _array[3] * zs;
xx = _array[0] * xs;
xy = _array[0] * ys;
xz = _array[0] * zs;
yy = _array[1] * ys;
yz = _array[1] * zs;
zz = _array[2] * zs;
m(0, 0) = T(T(1.0) - (yy + zz));
m(1, 0) = T(xy + wz);
m(2, 0) = T(xz - wy);
m(0, 1) = T(xy - wz);
m(1, 1) = T(T(1.0) - (xx + zz));
m(2, 1) = T(yz + wx);
m(0, 2) = T(xz + wy);
m(1, 2) = T(yz - wx);
m(2, 2) = T(T(1.0) - (xx + yy));
m(3, 0) = m(3, 1) = m(3, 2) = m(0, 3) = m(1, 3) = m(2, 3) = T(0.0);
m(3, 3) = T(1.0);
}
quaternion &set_value(const T *qp)
{
for (int i = 0; i < 4; i++) {
_array[i] = qp[i];
}
return *this;
}
quaternion &set_value(const matrix4<T> &m)
{
T tr, s;
int i, j, k;
const int nxt[3] = {1, 2, 0};
tr = m(0, 0) + m(1, 1) + m(2, 2);
if (tr > T(0)) {
s = T(sqrt(tr + m(3, 3)));
_array[3] = T(s * 0.5);
s = T(0.5) / s;
_array[0] = T((m(1, 2) - m(2, 1)) * s);
_array[1] = T((m(2, 0) - m(0, 2)) * s);
_array[2] = T((m(0, 1) - m(1, 0)) * s);
}
else {
i = 0;
if (m(1, 1) > m(0, 0)) {
i = 1;
}
if (m(2, 2) > m(i, i)) {
i = 2;
}
j = nxt[i];
k = nxt[j];
s = T(sqrt((m(i, j) - (m(j, j) + m(k, k))) + T(1.0)));
_array[i] = T(s * 0.5);
s = T(0.5 / s);
_array[3] = T((m(j, k) - m(k, j)) * s);
_array[j] = T((m(i, j) + m(j, i)) * s);
_array[k] = T((m(i, k) + m(k, i)) * s);
}
return *this;
}
quaternion &set_value(const vec3<T> &axis, T theta)
{
T sqnorm = square_norm(axis);
if (sqnorm == T(0.0)) {
// axis too small.
x = y = z = T(0.0);
w = T(1.0);
}
else {
theta *= T(0.5);
T sin_theta = T(sin(theta));
if (sqnorm != T(1)) {
sin_theta /= T(sqrt(sqnorm));
}
x = sin_theta * axis[0];
y = sin_theta * axis[1];
z = sin_theta * axis[2];
w = T(cos(theta));
}
return *this;
}
quaternion &set_value(const vec3<T> &rotateFrom, const vec3<T> &rotateTo)
{
vec3<T> p1, p2;
T alpha;
p1 = normalize(rotateFrom);
p2 = normalize(rotateTo);
alpha = dot(p1, p2);
if (alpha == T(1.0)) {
*this = quaternion();
return *this;
}
// ensures that the anti-parallel case leads to a positive dot
if (alpha == T(-1.0)) {
vec3<T> v;
if (p1[0] != p1[1] || p1[0] != p1[2]) {
v = vec3<T>(p1[1], p1[2], p1[0]);
}
else {
v = vec3<T>(-p1[0], p1[1], p1[2]);
}
v -= p1 * dot(p1, v);
v = normalize(v);
set_value(v, T(3.1415926));
return *this;
}
p1 = normalize(cross(p1, p2));
set_value(p1, T(acos(alpha)));
return *this;
}
quaternion &
set_value(const vec3<T> &from_look, const vec3<T> &from_up, const vec3<T> &to_look, const vec3<T> &to_up)
{
quaternion r_look = quaternion(from_look, to_look);
vec3<T> rotated_from_up(from_up);
r_look.mult_vec(rotated_from_up);
quaternion r_twist = quaternion(rotated_from_up, to_up);
*this = r_twist;
*this *= r_look;
return *this;
}
quaternion &operator*=(const quaternion<T> &qr)
{
quaternion ql(*this);
w = ql.w * qr.w - ql.x * qr.x - ql.y * qr.y - ql.z * qr.z;
x = ql.w * qr.x + ql.x * qr.w + ql.y * qr.z - ql.z * qr.y;
y = ql.w * qr.y + ql.y * qr.w + ql.z * qr.x - ql.x * qr.z;
z = ql.w * qr.z + ql.z * qr.w + ql.x * qr.y - ql.y * qr.x;
return *this;
}
friend quaternion normalize(const quaternion<T> &q)
{
quaternion r(q);
T rnorm = T(1.0) / T(sqrt(q.w * q.w + q.x * q.x + q.y * q.y + q.z * q.z));
r.x *= rnorm;
r.y *= rnorm;
r.z *= rnorm;
r.w *= rnorm;
}
friend quaternion<T> conjugate(const quaternion<T> &q)
{
quaternion<T> r(q);
r._array[0] *= T(-1.0);
r._array[1] *= T(-1.0);
r._array[2] *= T(-1.0);
return r;
}
friend quaternion<T> inverse(const quaternion<T> &q) { return conjugate(q); }
//
// Quaternion multiplication with cartesian vector
// v' = q*v*q(star)
//
void mult_vec(const vec3<T> &src, vec3<T> &dst) const
{
T v_coef = w * w - x * x - y * y - z * z;
T u_coef = T(2.0) * (src[0] * x + src[1] * y + src[2] * z);
T c_coef = T(2.0) * w;
dst.v[0] = v_coef * src.v[0] + u_coef * x + c_coef * (y * src.v[2] - z * src.v[1]);
dst.v[1] = v_coef * src.v[1] + u_coef * y + c_coef * (z * src.v[0] - x * src.v[2]);
dst.v[2] = v_coef * src.v[2] + u_coef * z + c_coef * (x * src.v[1] - y * src.v[0]);
}
void mult_vec(vec3<T> &src_and_dst) const { mult_vec(vec3<T>(src_and_dst), src_and_dst); }
void scale_angle(T scaleFactor)
{
vec3<T> axis;
T radians;
get_value(axis, radians);
radians *= scaleFactor;
set_value(axis, radians);
}
friend quaternion<T> slerp(const quaternion<T> &p, const quaternion<T> &q, T alpha)
{
quaternion r;
T cos_omega = p.x * q.x + p.y * q.y + p.z * q.z + p.w * q.w;
// if B is on opposite hemisphere from A, use -B instead
int bflip;
if ((bflip = (cos_omega < T(0)))) {
cos_omega = -cos_omega;
}
// complementary interpolation parameter
T beta = T(1) - alpha;
if (cos_omega >= T(1)) {
return p;
}
T omega = T(acos(cos_omega));
T one_over_sin_omega = T(1.0) / T(sin(omega));
beta = T(sin(omega * beta) * one_over_sin_omega);
alpha = T(sin(omega * alpha) * one_over_sin_omega);
if (bflip) {
alpha = -alpha;
}
r.x = beta * p._array[0] + alpha * q._array[0];
r.y = beta * p._array[1] + alpha * q._array[1];
r.z = beta * p._array[2] + alpha * q._array[2];
r.w = beta * p._array[3] + alpha * q._array[3];
return r;
}
T &operator[](int i) { return _array[i]; }
const T &operator[](int i) const { return _array[i]; }
friend bool operator==(const quaternion<T> &lhs, const quaternion<T> &rhs)
{
bool r = true;
for (int i = 0; i < 4; i++) {
r &= lhs._array[i] == rhs._array[i];
}
return r;
}
friend bool operator!=(const quaternion<T> &lhs, const quaternion<T> &rhs)
{
bool r = true;
for (int i = 0; i < 4; i++) {
r &= lhs._array[i] == rhs._array[i];
}
return r;
}
friend quaternion<T> operator*(const quaternion<T> &lhs, const quaternion<T> &rhs)
{
quaternion r(lhs);
r *= rhs;
return r;
}
union
{
struct
{
T x;
T y;
T z;
T w;
};
T _array[4];
};
};
}; // namespace nv
#endif

View File

@@ -0,0 +1,940 @@
/*
* Copyright 1993-2015 NVIDIA Corporation. All rights reserved.
*
* Please refer to the NVIDIA end user license agreement (EULA) associated
* with this source code for terms and conditions that govern your use of
* this software. Any use, reproduction, disclosure, or distribution of
* this software and related documentation outside the terms of the EULA
* is strictly prohibited.
*
*/
//
// Template math library for common 3D functionality
//
// nvVector.h - 2-vector, 3-vector, and 4-vector templates and utilities
//
// This code is in part deriver from glh, a cross platform glut helper library.
// The copyright for glh follows this notice.
//
// Copyright (c) NVIDIA Corporation. All rights reserved.
////////////////////////////////////////////////////////////////////////////////
/*
Copyright (c) 2000 Cass Everitt
Copyright (c) 2000 NVIDIA Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
* The names of contributors to this software may not be used
to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Cass Everitt - cass@r3.nu
*/
#ifndef NV_VECTOR_H
#define NV_VECTOR_H
namespace nv {
template <class T> class vec2;
template <class T> class vec3;
template <class T> class vec4;
//////////////////////////////////////////////////////////////////////
//
// vec2 - template class for 2-tuple vector
//
//////////////////////////////////////////////////////////////////////
template <class T> class vec2
{
public:
typedef T value_type;
int size() const { return 2; }
////////////////////////////////////////////////////////
//
// Constructors
//
////////////////////////////////////////////////////////
// Default/scalar constructor
vec2(const T &t = T())
{
for (int i = 0; i < size(); i++) {
_array[i] = t;
}
}
// Construct from array
vec2(const T *tp)
{
for (int i = 0; i < size(); i++) {
_array[i] = tp[i];
}
}
// Construct from explicit values
vec2(const T v0, const T v1)
{
x = v0;
y = v1;
}
explicit vec2(const vec3<T> &u)
{
for (int i = 0; i < size(); i++) {
_array[i] = u._array[i];
}
}
explicit vec2(const vec4<T> &u)
{
for (int i = 0; i < size(); i++) {
_array[i] = u._array[i];
}
}
const T *get_value() const { return _array; }
vec2<T> &set_value(const T *rhs)
{
for (int i = 0; i < size(); i++) {
_array[i] = rhs[i];
}
return *this;
}
// indexing operators
T &operator[](int i) { return _array[i]; }
const T &operator[](int i) const { return _array[i]; }
// type-cast operators
operator T *() { return _array; }
operator const T *() const { return _array; }
////////////////////////////////////////////////////////
//
// Math operators
//
////////////////////////////////////////////////////////
// scalar multiply assign
friend vec2<T> &operator*=(vec2<T> &lhs, T d)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] *= d;
}
return lhs;
}
// component-wise vector multiply assign
friend vec2<T> &operator*=(vec2<T> &lhs, const vec2<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] *= rhs[i];
}
return lhs;
}
// scalar divide assign
friend vec2<T> &operator/=(vec2<T> &lhs, T d)
{
if (d == 0) {
return lhs;
}
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] /= d;
}
return lhs;
}
// component-wise vector divide assign
friend vec2<T> &operator/=(vec2<T> &lhs, const vec2<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] /= rhs._array[i];
}
return lhs;
}
// component-wise vector add assign
friend vec2<T> &operator+=(vec2<T> &lhs, const vec2<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] += rhs._array[i];
}
return lhs;
}
// component-wise vector subtract assign
friend vec2<T> &operator-=(vec2<T> &lhs, const vec2<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] -= rhs._array[i];
}
return lhs;
}
// unary negate
friend vec2<T> operator-(const vec2<T> &rhs)
{
vec2<T> rv;
for (int i = 0; i < rhs.size(); i++) {
rv._array[i] = -rhs._array[i];
}
return rv;
}
// vector add
friend vec2<T> operator+(const vec2<T> &lhs, const vec2<T> &rhs)
{
vec2<T> rt(lhs);
return rt += rhs;
}
// vector subtract
friend vec2<T> operator-(const vec2<T> &lhs, const vec2<T> &rhs)
{
vec2<T> rt(lhs);
return rt -= rhs;
}
// scalar multiply
friend vec2<T> operator*(const vec2<T> &lhs, T rhs)
{
vec2<T> rt(lhs);
return rt *= rhs;
}
// scalar multiply
friend vec2<T> operator*(T lhs, const vec2<T> &rhs)
{
vec2<T> rt(lhs);
return rt *= rhs;
}
// vector component-wise multiply
friend vec2<T> operator*(const vec2<T> &lhs, const vec2<T> &rhs)
{
vec2<T> rt(lhs);
return rt *= rhs;
}
// scalar multiply
friend vec2<T> operator/(const vec2<T> &lhs, T rhs)
{
vec2<T> rt(lhs);
return rt /= rhs;
}
// vector component-wise multiply
friend vec2<T> operator/(const vec2<T> &lhs, const vec2<T> &rhs)
{
vec2<T> rt(lhs);
return rt /= rhs;
}
////////////////////////////////////////////////////////
//
// Comparison operators
//
////////////////////////////////////////////////////////
// equality
friend bool operator==(const vec2<T> &lhs, const vec2<T> &rhs)
{
bool r = true;
for (int i = 0; i < lhs.size(); i++) {
r &= lhs._array[i] == rhs._array[i];
}
return r;
}
// inequality
friend bool operator!=(const vec2<T> &lhs, const vec2<T> &rhs)
{
bool r = true;
for (int i = 0; i < lhs.size(); i++) {
r &= lhs._array[i] != rhs._array[i];
}
return r;
}
// data intentionally left public to allow vec2.x
union
{
struct
{
T x, y; // standard names for components
};
struct
{
T s, t; // standard names for components
};
T _array[2]; // array access
};
};
//////////////////////////////////////////////////////////////////////
//
// vec3 - template class for 3-tuple vector
//
//////////////////////////////////////////////////////////////////////
template <class T> class vec3
{
public:
typedef T value_type;
int size() const { return 3; }
////////////////////////////////////////////////////////
//
// Constructors
//
////////////////////////////////////////////////////////
// Default/scalar constructor
vec3(const T &t = T())
{
for (int i = 0; i < size(); i++) {
_array[i] = t;
}
}
// Construct from array
vec3(const T *tp)
{
for (int i = 0; i < size(); i++) {
_array[i] = tp[i];
}
}
// Construct from explicit values
vec3(const T v0, const T v1, const T v2)
{
x = v0;
y = v1;
z = v2;
}
explicit vec3(const vec4<T> &u)
{
for (int i = 0; i < size(); i++) {
_array[i] = u._array[i];
}
}
explicit vec3(const vec2<T> &u, T v0)
{
x = u.x;
y = u.y;
z = v0;
}
const T *get_value() const { return _array; }
vec3<T> &set_value(const T *rhs)
{
for (int i = 0; i < size(); i++) {
_array[i] = rhs[i];
}
return *this;
}
// indexing operators
T &operator[](int i) { return _array[i]; }
const T &operator[](int i) const { return _array[i]; }
// type-cast operators
operator T *() { return _array; }
operator const T *() const { return _array; }
////////////////////////////////////////////////////////
//
// Math operators
//
////////////////////////////////////////////////////////
// scalar multiply assign
friend vec3<T> &operator*=(vec3<T> &lhs, T d)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] *= d;
}
return lhs;
}
// component-wise vector multiply assign
friend vec3<T> &operator*=(vec3<T> &lhs, const vec3<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] *= rhs[i];
}
return lhs;
}
// scalar divide assign
friend vec3<T> &operator/=(vec3<T> &lhs, T d)
{
if (d == 0) {
return lhs;
}
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] /= d;
}
return lhs;
}
// component-wise vector divide assign
friend vec3<T> &operator/=(vec3<T> &lhs, const vec3<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] /= rhs._array[i];
}
return lhs;
}
// component-wise vector add assign
friend vec3<T> &operator+=(vec3<T> &lhs, const vec3<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] += rhs._array[i];
}
return lhs;
}
// component-wise vector subtract assign
friend vec3<T> &operator-=(vec3<T> &lhs, const vec3<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] -= rhs._array[i];
}
return lhs;
}
// unary negate
friend vec3<T> operator-(const vec3<T> &rhs)
{
vec3<T> rv;
for (int i = 0; i < rhs.size(); i++) {
rv._array[i] = -rhs._array[i];
}
return rv;
}
// vector add
friend vec3<T> operator+(const vec3<T> &lhs, const vec3<T> &rhs)
{
vec3<T> rt(lhs);
return rt += rhs;
}
// vector subtract
friend vec3<T> operator-(const vec3<T> &lhs, const vec3<T> &rhs)
{
vec3<T> rt(lhs);
return rt -= rhs;
}
// scalar multiply
friend vec3<T> operator*(const vec3<T> &lhs, T rhs)
{
vec3<T> rt(lhs);
return rt *= rhs;
}
// scalar multiply
friend vec3<T> operator*(T lhs, const vec3<T> &rhs)
{
vec3<T> rt(lhs);
return rt *= rhs;
}
// vector component-wise multiply
friend vec3<T> operator*(const vec3<T> &lhs, const vec3<T> &rhs)
{
vec3<T> rt(lhs);
return rt *= rhs;
}
// scalar multiply
friend vec3<T> operator/(const vec3<T> &lhs, T rhs)
{
vec3<T> rt(lhs);
return rt /= rhs;
}
// vector component-wise multiply
friend vec3<T> operator/(const vec3<T> &lhs, const vec3<T> &rhs)
{
vec3<T> rt(lhs);
return rt /= rhs;
}
////////////////////////////////////////////////////////
//
// Comparison operators
//
////////////////////////////////////////////////////////
// equality
friend bool operator==(const vec3<T> &lhs, const vec3<T> &rhs)
{
bool r = true;
for (int i = 0; i < lhs.size(); i++) {
r &= lhs._array[i] == rhs._array[i];
}
return r;
}
// inequality
friend bool operator!=(const vec3<T> &lhs, const vec3<T> &rhs)
{
bool r = true;
for (int i = 0; i < lhs.size(); i++) {
r &= lhs._array[i] != rhs._array[i];
}
return r;
}
////////////////////////////////////////////////////////////////////////////////
//
// dimension specific operations
//
////////////////////////////////////////////////////////////////////////////////
// cross product
friend vec3<T> cross(const vec3<T> &lhs, const vec3<T> &rhs)
{
vec3<T> r;
r.x = lhs.y * rhs.z - lhs.z * rhs.y;
r.y = lhs.z * rhs.x - lhs.x * rhs.z;
r.z = lhs.x * rhs.y - lhs.y * rhs.x;
return r;
}
// data intentionally left public to allow vec2.x
union
{
struct
{
T x, y, z; // standard names for components
};
struct
{
T s, t, r; // standard names for components
};
T _array[3]; // array access
};
};
//////////////////////////////////////////////////////////////////////
//
// vec4 - template class for 4-tuple vector
//
//////////////////////////////////////////////////////////////////////
template <class T> class vec4
{
public:
typedef T value_type;
int size() const { return 4; }
////////////////////////////////////////////////////////
//
// Constructors
//
////////////////////////////////////////////////////////
// Default/scalar constructor
vec4(const T &t = T())
{
for (int i = 0; i < size(); i++) {
_array[i] = t;
}
}
// Construct from array
vec4(const T *tp)
{
for (int i = 0; i < size(); i++) {
_array[i] = tp[i];
}
}
// Construct from explicit values
vec4(const T v0, const T v1, const T v2, const T v3)
{
x = v0;
y = v1;
z = v2;
w = v3;
}
explicit vec4(const vec3<T> &u, T v0)
{
x = u.x;
y = u.y;
z = u.z;
w = v0;
}
explicit vec4(const vec2<T> &u, T v0, T v1)
{
x = u.x;
y = u.y;
z = v0;
w = v1;
}
const T *get_value() const { return _array; }
vec4<T> &set_value(const T *rhs)
{
for (int i = 0; i < size(); i++) {
_array[i] = rhs[i];
}
return *this;
}
// indexing operators
T &operator[](int i) { return _array[i]; }
const T &operator[](int i) const { return _array[i]; }
// type-cast operators
operator T *() { return _array; }
operator const T *() const { return _array; }
////////////////////////////////////////////////////////
//
// Math operators
//
////////////////////////////////////////////////////////
// scalar multiply assign
friend vec4<T> &operator*=(vec4<T> &lhs, T d)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] *= d;
}
return lhs;
}
// component-wise vector multiply assign
friend vec4<T> &operator*=(vec4<T> &lhs, const vec4<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] *= rhs[i];
}
return lhs;
}
// scalar divide assign
friend vec4<T> &operator/=(vec4<T> &lhs, T d)
{
if (d == 0) {
return lhs;
}
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] /= d;
}
return lhs;
}
// component-wise vector divide assign
friend vec4<T> &operator/=(vec4<T> &lhs, const vec4<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] /= rhs._array[i];
}
return lhs;
}
// component-wise vector add assign
friend vec4<T> &operator+=(vec4<T> &lhs, const vec4<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] += rhs._array[i];
}
return lhs;
}
// component-wise vector subtract assign
friend vec4<T> &operator-=(vec4<T> &lhs, const vec4<T> &rhs)
{
for (int i = 0; i < lhs.size(); i++) {
lhs._array[i] -= rhs._array[i];
}
return lhs;
}
// unary negate
friend vec4<T> operator-(const vec4<T> &rhs)
{
vec4<T> rv;
for (int i = 0; i < rhs.size(); i++) {
rv._array[i] = -rhs._array[i];
}
return rv;
}
// vector add
friend vec4<T> operator+(const vec4<T> &lhs, const vec4<T> &rhs)
{
vec4<T> rt(lhs);
return rt += rhs;
}
// vector subtract
friend vec4<T> operator-(const vec4<T> &lhs, const vec4<T> &rhs)
{
vec4<T> rt(lhs);
return rt -= rhs;
}
// scalar multiply
friend vec4<T> operator*(const vec4<T> &lhs, T rhs)
{
vec4<T> rt(lhs);
return rt *= rhs;
}
// scalar multiply
friend vec4<T> operator*(T lhs, const vec4<T> &rhs)
{
vec4<T> rt(lhs);
return rt *= rhs;
}
// vector component-wise multiply
friend vec4<T> operator*(const vec4<T> &lhs, const vec4<T> &rhs)
{
vec4<T> rt(lhs);
return rt *= rhs;
}
// scalar multiply
friend vec4<T> operator/(const vec4<T> &lhs, T rhs)
{
vec4<T> rt(lhs);
return rt /= rhs;
}
// vector component-wise multiply
friend vec4<T> operator/(const vec4<T> &lhs, const vec4<T> &rhs)
{
vec4<T> rt(lhs);
return rt /= rhs;
}
////////////////////////////////////////////////////////
//
// Comparison operators
//
////////////////////////////////////////////////////////
// equality
friend bool operator==(const vec4<T> &lhs, const vec4<T> &rhs)
{
bool r = true;
for (int i = 0; i < lhs.size(); i++) {
r &= lhs._array[i] == rhs._array[i];
}
return r;
}
// inequality
friend bool operator!=(const vec4<T> &lhs, const vec4<T> &rhs)
{
bool r = true;
for (int i = 0; i < lhs.size(); i++) {
r &= lhs._array[i] != rhs._array[i];
}
return r;
}
// data intentionally left public to allow vec2.x
union
{
struct
{
T x, y, z, w; // standard names for components
};
struct
{
T s, t, r, q; // standard names for components
};
T _array[4]; // array access
};
};
////////////////////////////////////////////////////////////////////////////////
//
// Generic vector operations
//
////////////////////////////////////////////////////////////////////////////////
// compute the dot product of two vectors
template <class T> inline typename T::value_type dot(const T &lhs, const T &rhs)
{
typename T::value_type r = 0;
for (int i = 0; i < lhs.size(); i++) {
r += lhs._array[i] * rhs._array[i];
}
return r;
}
// return the length of the provided vector
template <class T> inline typename T::value_type length(const T &vec)
{
typename T::value_type r = 0;
for (int i = 0; i < vec.size(); i++) {
r += vec._array[i] * vec._array[i];
}
return typename T::value_type(sqrt(r));
}
// return the squared norm
template <class T> inline typename T::value_type square_norm(const T &vec)
{
typename T::value_type r = 0;
for (int i = 0; i < vec.size(); i++) {
r += vec._array[i] * vec._array[i];
}
return r;
}
// return the normalized version of the vector
template <class T> inline T normalize(const T &vec)
{
typename T::value_type sum(0);
T r;
for (int i = 0; i < vec.size(); i++) {
sum += vec._array[i] * vec._array[i];
}
sum = typename T::value_type(sqrt(sum));
if (sum > 0)
for (int i = 0; i < vec.size(); i++) {
r._array[i] = vec._array[i] / sum;
}
return r;
}
// In VC8 : min and max are already defined by a #define...
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
// componentwise min
template <class T> inline T min(const T &lhs, const T &rhs)
{
T rt;
for (int i = 0; i < lhs.size(); i++) {
rt._array[i] = std::min(lhs._array[i], rhs._array[i]);
}
return rt;
}
// componentwise max
template <class T> inline T max(const T &lhs, const T &rhs)
{
T rt;
for (int i = 0; i < lhs.size(); i++) {
rt._array[i] = std::max(lhs._array[i], rhs._array[i]);
}
return rt;
}
}; // namespace nv
#endif

View File

@@ -0,0 +1,946 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
CUDA particle system with volumetric shadows
sgreen 11/2008
This sample demonstrates a technique for rendering realistic volumetric
shadows through a cloud of particles. It uses CUDA for the simulation and
depth sorting of the particles, and OpenGL for rendering.
See the accompanying documentation for more details on the algorithm.
This file handles OpenGL initialization and the user interface.
*/
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <helper_gl.h>
#include <math.h>
#if defined(__APPLE__) || defined(__MACOSX)
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include <GLUT/glut.h>
#else
#include <GL/freeglut.h>
#endif
#if defined(WIN32)
#include <GL/wglew.h>
#endif
#include <cuda_gl_interop.h>
#include <cuda_runtime.h>
#include <helper_cuda.h>
#include "GLSLProgram.h"
#include "ParticleSystem.cuh"
#include "ParticleSystem.h"
#include "SmokeRenderer.h"
#include "SmokeShaders.h"
#include "paramgl.h"
uint numParticles = 1 << 16;
ParticleSystem *psystem = 0;
SmokeRenderer *renderer = 0;
GLSLProgram *floorProg = 0;
int winWidth = 1280, winHeight = 1024;
int g_TotalErrors = 0;
// view params
int ox, oy;
int buttonState = 0;
bool keyDown[256];
vec3f cameraPos(0, -1, -4);
vec3f cameraRot(0, 0, 0);
vec3f cameraPosLag(cameraPos);
vec3f cameraRotLag(cameraRot);
vec3f cursorPos(0, 1, 0);
vec3f cursorPosLag(cursorPos);
vec3f lightPos(5.0, 5.0, -5.0);
const float inertia = 0.1f;
const float translateSpeed = 0.002f;
const float cursorSpeed = 0.01f;
const float rotateSpeed = 0.2f;
const float walkSpeed = 0.05f;
enum { M_VIEW = 0, M_MOVE_CURSOR, M_MOVE_LIGHT };
int mode = 0;
int displayMode = (int)SmokeRenderer::VOLUMETRIC;
// QA AutoTest
bool g_bQAReadback = false;
// toggles
bool displayEnabled = true;
bool paused = false;
bool displaySliders = false;
bool wireframe = false;
bool animateEmitter = true;
bool emitterOn = true;
bool sort = true;
bool displayLightBuffer = false;
bool drawVectors = false;
bool doBlur = false;
float emitterVel = 0.0f;
uint emitterRate = 1000;
float emitterRadius = 0.25;
float emitterSpread = 0.0;
uint emitterIndex = 0;
// simulation parameters
float timestep = 0.5f;
float currentTime = 0.0f;
float spriteSize = 0.05f;
float alpha = 0.1f;
float shadowAlpha = 0.02f;
float particleLifetime = (float)numParticles / (float)emitterRate;
vec3f lightColor(1.0f, 1.0f, 0.8f);
vec3f colorAttenuation(0.5f, 0.75f, 1.0f);
float blurRadius = 2.0f;
int numSlices = 64;
int numDisplayedSlices = numSlices;
// fps
static int fpsCount = 0;
static int fpsLimit = 1;
StopWatchInterface *timer = NULL;
float modelView[16];
ParamListGL *params;
GLuint floorTex = 0;
// CheckRender object for verification
#define MAX_EPSILON_ERROR 10.0f
#define THRESHOLD 0.40f
// Define the files that are to be saved and the reference images for validation
const char *sSDKsample = "CUDA Smoke Particles";
const char *sRefBin[] = {"ref_smokePart_pos.bin", "ref_smokePart_vel.bin", NULL};
void runEmitter();
// initialize particle system
void initParticles(int numParticles, bool bUseVBO, bool bUseGL)
{
psystem = new ParticleSystem(numParticles, bUseVBO, bUseGL);
psystem->reset(ParticleSystem::CONFIG_RANDOM);
if (bUseVBO) {
renderer = new SmokeRenderer(numParticles);
renderer->setLightTarget(vec3f(0.0, 1.0, 0.0));
sdkCreateTimer(&timer);
}
}
void cleanup()
{
if (psystem) {
delete psystem;
}
if (renderer) {
delete renderer;
}
if (floorProg) {
delete floorProg;
}
sdkDeleteTimer(&timer);
if (params) {
delete params;
}
if (floorTex) {
glDeleteTextures(1, &floorTex);
}
}
void renderScene()
{
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
// draw floor
floorProg->enable();
floorProg->bindTexture("tex", floorTex, GL_TEXTURE_2D, 0);
floorProg->bindTexture("shadowTex", renderer->getShadowTexture(), GL_TEXTURE_2D, 1);
floorProg->setUniformfv("lightPosEye", renderer->getLightPositionEyeSpace(), 3);
floorProg->setUniformfv("lightColor", lightColor, 3);
// set shadow matrix as texture matrix
matrix4f shadowMatrix = renderer->getShadowMatrix();
glActiveTexture(GL_TEXTURE0);
glMatrixMode(GL_TEXTURE);
glLoadMatrixf((GLfloat *)shadowMatrix.get_value());
glColor3f(1.0, 1.0, 1.0);
glNormal3f(0.0, 1.0, 0.0);
glBegin(GL_QUADS);
{
float s = 20.f;
float rep = 20.f;
glTexCoord2f(0.f, 0.f);
glVertex3f(-s, 0, -s);
glTexCoord2f(rep, 0.f);
glVertex3f(s, 0, -s);
glTexCoord2f(rep, rep);
glVertex3f(s, 0, s);
glTexCoord2f(0.f, rep);
glVertex3f(-s, 0, s);
}
glEnd();
floorProg->disable();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
// draw light
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(lightPos.x, lightPos.y, lightPos.z);
glColor3fv(&lightColor[0]);
glutSolidSphere(0.1, 10, 5);
glPopMatrix();
}
// main rendering loop
void display()
{
sdkStartTimer(&timer);
// move camera
if (cameraPos[1] > 0.0f) {
cameraPos[1] = 0.0f;
}
cameraPosLag += (cameraPos - cameraPosLag) * inertia;
cameraRotLag += (cameraRot - cameraRotLag) * inertia;
cursorPosLag += (cursorPos - cursorPosLag) * inertia;
// view transform
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(cameraRotLag[0], 1.0, 0.0, 0.0);
glRotatef(cameraRotLag[1], 0.0, 1.0, 0.0);
glTranslatef(cameraPosLag[0], cameraPosLag[1], cameraPosLag[2]);
glGetFloatv(GL_MODELVIEW_MATRIX, modelView);
// update the simulation
if (!paused) {
if (emitterOn) {
runEmitter();
}
SimParams &p = psystem->getParams();
p.cursorPos = make_float3(cursorPosLag.x, cursorPosLag.y, cursorPosLag.z);
psystem->step(timestep);
currentTime += timestep;
}
renderer->calcVectors();
vec3f sortVector = renderer->getSortVector();
psystem->setSortVector(make_float3(sortVector.x, sortVector.y, sortVector.z));
psystem->setModelView(modelView);
psystem->setSorting(sort);
psystem->depthSort();
// render
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
renderScene();
// draw particles
if (displayEnabled) {
// render scene to offscreen buffers to get correct occlusion
renderer->beginSceneRender(SmokeRenderer::LIGHT_BUFFER);
renderScene();
renderer->endSceneRender(SmokeRenderer::LIGHT_BUFFER);
renderer->beginSceneRender(SmokeRenderer::SCENE_BUFFER);
renderScene();
renderer->endSceneRender(SmokeRenderer::SCENE_BUFFER);
renderer->setPositionBuffer(psystem->getPosBuffer());
renderer->setVelocityBuffer(psystem->getVelBuffer());
renderer->setIndexBuffer(psystem->getSortedIndexBuffer());
renderer->setNumParticles(psystem->getNumParticles());
renderer->setParticleRadius(spriteSize);
renderer->setDisplayLightBuffer(displayLightBuffer);
renderer->setAlpha(alpha);
renderer->setShadowAlpha(shadowAlpha);
renderer->setLightPosition(lightPos);
renderer->setColorAttenuation(colorAttenuation);
renderer->setLightColor(lightColor);
renderer->setNumSlices(numSlices);
renderer->setNumDisplayedSlices(numDisplayedSlices);
renderer->setBlurRadius(blurRadius);
renderer->render();
if (drawVectors) {
renderer->debugVectors();
}
}
// display sliders
if (displaySliders) {
glDisable(GL_DEPTH_TEST);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); // invert color
glEnable(GL_BLEND);
params->Render(0, 0);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
glutSwapBuffers();
glutReportErrors();
sdkStopTimer(&timer);
fpsCount++;
// this displays the frame rate updated every second (independent of frame
// rate)
if (fpsCount >= fpsLimit) {
char fps[256];
float ifps = 1.f / (sdkGetAverageTimerValue(&timer) / 1000.f);
sprintf(fps, "CUDA Smoke Particles (%d particles): %3.1f fps", numParticles, ifps);
glutSetWindowTitle(fps);
fpsCount = 0;
fpsLimit = (ifps > 1.f) ? (int)ifps : 1;
if (paused) {
fpsLimit = 0;
}
sdkResetTimer(&timer);
}
}
// GLUT callback functions
void reshape(int w, int h)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (float)w / (float)h, 0.01, 100.0);
glMatrixMode(GL_MODELVIEW);
glViewport(0, 0, w, h);
renderer->setFOV(60.0);
renderer->setWindowSize(w, h);
}
void mouse(int button, int state, int x, int y)
{
int mods;
if (state == GLUT_DOWN) {
buttonState |= 1 << button;
}
else if (state == GLUT_UP) {
buttonState = 0;
}
mods = glutGetModifiers();
if (mods & GLUT_ACTIVE_SHIFT) {
buttonState = 2;
}
else if (mods & GLUT_ACTIVE_CTRL) {
buttonState = 3;
}
ox = x;
oy = y;
if (displaySliders) {
if (params->Mouse(x, y, button, state)) {
glutPostRedisplay();
return;
}
}
glutPostRedisplay();
}
// transform vector by matrix
void xform(vec3f &v, vec3f &r, float *m)
{
r.x = v.x * m[0] + v.y * m[4] + v.z * m[8] + m[12];
r.y = v.x * m[1] + v.y * m[5] + v.z * m[9] + m[13];
r.z = v.x * m[2] + v.y * m[6] + v.z * m[10] + m[14];
}
// transform vector by transpose of matrix (assuming orthonormal)
void ixform(vec3f &v, vec3f &r, float *m)
{
r.x = v.x * m[0] + v.y * m[1] + v.z * m[2];
r.y = v.x * m[4] + v.y * m[5] + v.z * m[6];
r.z = v.x * m[8] + v.y * m[9] + v.z * m[10];
}
void ixformPoint(vec3f &v, vec3f &r, float *m)
{
vec3f x;
x.x = v.x - m[12];
x.y = v.y - m[13];
x.z = v.z - m[14];
ixform(x, r, m);
}
void motion(int x, int y)
{
float dx, dy;
dx = (float)(x - ox);
dy = (float)(y - oy);
if (displaySliders) {
if (params->Motion(x, y)) {
ox = x;
oy = y;
glutPostRedisplay();
return;
}
}
switch (mode) {
case M_VIEW: {
if (buttonState == 1) {
// left = rotate
cameraRot[0] += dy * rotateSpeed;
cameraRot[1] += dx * rotateSpeed;
}
if (buttonState == 2) {
// middle = translate
vec3f v = vec3f(dx * translateSpeed, -dy * translateSpeed, 0.0f);
vec3f r;
ixform(v, r, modelView);
cameraPos += r;
}
if (buttonState == 3) {
// left+middle = zoom
vec3f v = vec3f(0.0, 0.0, dy * translateSpeed);
vec3f r;
ixform(v, r, modelView);
cameraPos += r;
}
} break;
case M_MOVE_CURSOR: {
if (buttonState == 1) {
vec3f v = vec3f(dx * cursorSpeed, -dy * cursorSpeed, 0.0f);
vec3f r;
ixform(v, r, modelView);
cursorPos += r;
}
else if (buttonState == 2) {
vec3f v = vec3f(0.0f, 0.0f, dy * cursorSpeed);
vec3f r;
ixform(v, r, modelView);
cursorPos += r;
}
} break;
case M_MOVE_LIGHT:
if (buttonState == 1) {
vec3f v = vec3f(dx * cursorSpeed, -dy * cursorSpeed, 0.0f);
vec3f r;
ixform(v, r, modelView);
lightPos += r;
}
else if (buttonState == 2) {
vec3f v = vec3f(0.0f, 0.0f, dy * cursorSpeed);
vec3f r;
ixform(v, r, modelView);
lightPos += r;
}
break;
}
ox = x;
oy = y;
glutPostRedisplay();
}
// commented out to remove unused parameter warnings in Linux
void key(unsigned char key, int /*x*/, int /*y*/)
{
switch (key) {
case ' ':
paused = !paused;
break;
case 13:
psystem->step(timestep);
renderer->setPositionBuffer(psystem->getPosBuffer());
renderer->setVelocityBuffer(psystem->getVelBuffer());
break;
case '\033':
cleanup();
exit(EXIT_SUCCESS);
break;
case 'v':
mode = M_VIEW;
animateEmitter = true;
break;
case 'm':
mode = M_MOVE_CURSOR;
animateEmitter = false;
break;
case 'l':
mode = M_MOVE_LIGHT;
break;
case 'r':
displayEnabled = !displayEnabled;
break;
case '1':
psystem->reset(ParticleSystem::CONFIG_RANDOM);
break;
case '2':
emitterOn ^= 1;
break;
case 'W':
wireframe = !wireframe;
break;
case 'h':
displaySliders = !displaySliders;
break;
case 'o':
sort ^= 1;
psystem->setSorting(sort);
break;
case 'D':
displayLightBuffer ^= 1;
break;
case 'p':
displayMode = (displayMode + 1) % SmokeRenderer::NUM_MODES;
renderer->setDisplayMode((SmokeRenderer::DisplayMode)displayMode);
break;
case 'P':
displayMode--;
if (displayMode < 0) {
displayMode = SmokeRenderer::NUM_MODES - 1;
}
renderer->setDisplayMode((SmokeRenderer::DisplayMode)displayMode);
break;
case 'V':
drawVectors ^= 1;
break;
case '=':
numSlices *= 2;
if (numSlices > 256) {
numSlices = 256;
}
numDisplayedSlices = numSlices;
break;
case '-':
if (numSlices > 1) {
numSlices /= 2;
}
numDisplayedSlices = numSlices;
break;
case 'b':
doBlur ^= 1;
renderer->setDoBlur(doBlur);
break;
}
printf("numSlices = %d\n", numSlices);
keyDown[key] = true;
glutPostRedisplay();
}
void keyUp(unsigned char key, int /*x*/, int /*y*/) { keyDown[key] = false; }
void runEmitter()
{
vec3f vel = vec3f(0, emitterVel, 0);
vec3f vx(1, 0, 0);
vec3f vy(0, 0, 1);
vec3f spread(emitterSpread, 0.0f, emitterSpread);
psystem->sphereEmitter(emitterIndex,
cursorPosLag,
vel,
spread,
emitterRadius,
ftoi(emitterRate * timestep),
particleLifetime,
particleLifetime * 0.1f);
if (emitterIndex > numParticles - 1) {
emitterIndex = 0;
}
}
void special(int k, int x, int y)
{
if (displaySliders) {
params->Special(k, x, y);
}
}
void idle(void)
{
// move camera in view direction
/*
0 4 8 12 x
1 5 9 13 y
2 6 10 14 z
*/
if (keyDown['w']) {
cameraPos[0] += modelView[2] * walkSpeed;
cameraPos[1] += modelView[6] * walkSpeed;
cameraPos[2] += modelView[10] * walkSpeed;
}
if (keyDown['s']) {
cameraPos[0] -= modelView[2] * walkSpeed;
cameraPos[1] -= modelView[6] * walkSpeed;
cameraPos[2] -= modelView[10] * walkSpeed;
}
if (keyDown['a']) {
cameraPos[0] += modelView[0] * walkSpeed;
cameraPos[1] += modelView[4] * walkSpeed;
cameraPos[2] += modelView[8] * walkSpeed;
}
if (keyDown['d']) {
cameraPos[0] -= modelView[0] * walkSpeed;
cameraPos[1] -= modelView[4] * walkSpeed;
cameraPos[2] -= modelView[8] * walkSpeed;
}
if (keyDown['e']) {
cameraPos[0] += modelView[1] * walkSpeed;
cameraPos[1] += modelView[5] * walkSpeed;
cameraPos[2] += modelView[9] * walkSpeed;
}
if (keyDown['q']) {
cameraPos[0] -= modelView[1] * walkSpeed;
cameraPos[1] -= modelView[5] * walkSpeed;
cameraPos[2] -= modelView[9] * walkSpeed;
}
if (animateEmitter) {
const float speed = 0.02f;
cursorPos.x = sin(currentTime * speed) * 1.5f;
cursorPos.y = 1.5f + sin(currentTime * speed * 1.3f);
cursorPos.z = cos(currentTime * speed) * 1.5f;
}
glutPostRedisplay();
}
// initialize sliders
void initParams()
{
// create a new parameter list
params = new ParamListGL("misc");
params->AddParam(new Param<int>("displayed slices", numDisplayedSlices, 0, 256, 1, &numDisplayedSlices));
params->AddParam(new Param<float>("time step", timestep, 0.0f, 1.0f, 0.001f, &timestep));
SimParams &p = psystem->getParams();
params->AddParam(new Param<float>("damping", 0.99f, 0.0f, 1.0f, 0.001f, &p.globalDamping));
params->AddParam(new Param<float>("gravity", 0.0f, 0.01f, -0.01f, 0.0001f, &p.gravity.y));
params->AddParam(new Param<float>("noise freq", 0.1f, 0.0f, 1.0f, 0.001f, &p.noiseFreq));
params->AddParam(new Param<float>("noise strength", 0.001f, 0.0f, 0.01f, 0.001f, &p.noiseAmp));
params->AddParam(new Param<float>("noise anim", 0.0f, -0.001f, 0.001f, 0.0001f, &p.noiseSpeed.y));
params->AddParam(new Param<float>("sprite size", spriteSize, 0.0f, 0.1f, 0.001f, &spriteSize));
params->AddParam(new Param<float>("alpha", alpha, 0.0f, 1.0f, 0.001f, &alpha));
params->AddParam(new Param<float>("light color r", lightColor[0], 0.0f, 1.0f, 0.01f, &lightColor[0]));
params->AddParam(new Param<float>("light color g", lightColor[1], 0.0f, 1.0f, 0.01f, &lightColor[1]));
params->AddParam(new Param<float>("light color b", lightColor[2], 0.0f, 1.0f, 0.01f, &lightColor[2]));
params->AddParam(new Param<float>("atten color r", colorAttenuation[0], 0.0f, 1.0f, 0.01f, &colorAttenuation[0]));
params->AddParam(new Param<float>("atten color g", colorAttenuation[1], 0.0f, 1.0f, 0.01f, &colorAttenuation[1]));
params->AddParam(new Param<float>("atten color b", colorAttenuation[2], 0.0f, 1.0f, 0.01f, &colorAttenuation[2]));
params->AddParam(new Param<float>("shadow alpha", shadowAlpha, 0.0f, 0.1f, 0.001f, &shadowAlpha));
params->AddParam(new Param<float>("blur radius", blurRadius, 0.0f, 10.0f, 0.1f, &blurRadius));
params->AddParam(new Param<float>("emitter radius", emitterRadius, 0.0f, 2.0f, 0.01f, &emitterRadius));
params->AddParam(new Param<uint>("emitter rate", emitterRate, 0, 10000, 1, &emitterRate));
params->AddParam(new Param<float>("emitter velocity", emitterVel, 0.0f, 0.1f, 0.001f, &emitterVel));
params->AddParam(new Param<float>("emitter spread", emitterSpread, 0.0f, 0.1f, 0.001f, &emitterSpread));
params->AddParam(new Param<float>("particle lifetime", particleLifetime, 0.0f, 1000.0f, 1.0f, &particleLifetime));
}
void mainMenu(int i) { key((unsigned char)i, 0, 0); }
void initMenus()
{
glutCreateMenu(mainMenu);
glutAddMenuEntry("Reset block [1]", '1');
glutAddMenuEntry("Toggle emitter [2]", '2');
glutAddMenuEntry("Toggle animation [ ]", ' ');
glutAddMenuEntry("Step animation [ret]", 13);
glutAddMenuEntry("View mode [v]", 'v');
glutAddMenuEntry("Move cursor mode [m]", 'm');
glutAddMenuEntry("Move light mode [l]", 'l');
glutAddMenuEntry("Toggle point rendering [p]", 'p');
glutAddMenuEntry("Toggle sliders [h]", 'h');
glutAddMenuEntry("Toggle sorting [o]", 'o');
glutAddMenuEntry("Toggle vectors [V]", 'V');
glutAddMenuEntry("Display light buffer [D]", 'D');
glutAddMenuEntry("Toggle shadow blur [b]", 'b');
glutAddMenuEntry("Increase no. slices [=]", '=');
glutAddMenuEntry("Decrease no. slices [-]", '-');
glutAddMenuEntry("Quit (esc)", '\033');
glutAttachMenu(GLUT_RIGHT_BUTTON);
}
GLuint createTexture(GLenum target, GLint internalformat, GLenum format, int w, int h, void *data)
{
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(target, tex);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(target, 0, internalformat, w, h, 0, format, GL_UNSIGNED_BYTE, data);
return tex;
}
GLuint loadTexture(char *filename)
{
unsigned char *data = 0;
unsigned int width, height;
sdkLoadPPM4ub(filename, &data, &width, &height);
if (!data) {
printf("Error opening file '%s'\n", filename);
return 0;
}
printf("Loaded '%s', %d x %d pixels\n", filename, width, height);
return createTexture(GL_TEXTURE_2D, GL_RGBA8, GL_RGBA, width, height, data);
}
// initialize OpenGL
void initGL(int *argc, char **argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("CUDA Smoke Particles");
if (!isGLVersionSupported(2, 0)) {
fprintf(stderr,
"The following required OpenGL extensions "
"missing:\n\tGL_VERSION_2_0\n\tGL_VERSION_1_5\n");
exit(EXIT_SUCCESS);
}
if (!areGLExtensionsSupported("GL_ARB_multitexture "
"GL_ARB_vertex_buffer_object "
"GL_EXT_geometry_shader4")) {
fprintf(stderr,
"The following required OpenGL extensions "
"missing:\n\tGL_ARB_multitexture\n\tGL_ARB_vertex_buffer_"
"object\n\tGL_EXT_geometry_shader4.\n");
exit(EXIT_SUCCESS);
}
#if defined(WIN32)
if (wglewIsSupported("WGL_EXT_swap_control")) {
// disable vertical sync
wglSwapIntervalEXT(0);
}
#endif
glEnable(GL_DEPTH_TEST);
// load floor texture
char *imagePath = sdkFindFilePath("floortile.ppm", argv[0]);
if (imagePath == NULL) {
fprintf(stderr, "Error finding floor image file\n");
exit(EXIT_FAILURE);
}
floorTex = loadTexture(imagePath);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f);
floorProg = new GLSLProgram(floorVS, floorPS);
glutReportErrors();
}
////////////////////////////////////////////////////////////////////////////////
// Program main
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
#if defined(__linux__)
setenv("DISPLAY", ":0", 0);
#endif
printf("%s Starting...\n\n", sSDKsample);
printf("NOTE: The CUDA Samples are not meant for performance measurements. "
"Results may vary when GPU Boost is enabled.\n\n");
if (argc > 1) {
if (checkCmdLineFlag(argc, (const char **)argv, "n")) {
numParticles = getCmdLineArgumentInt(argc, (const char **)argv, "n");
}
if (checkCmdLineFlag(argc, (const char **)argv, "qatest")) {
g_bQAReadback = true;
}
}
if (g_bQAReadback) {
// For Automated testing, we do not use OpenGL/CUDA interop
findCudaDevice(argc, (const char **)argv);
// This code path is used for Automated Testing
initParticles(numParticles, false, false);
initParams();
if (emitterOn) {
runEmitter();
}
SimParams &params = psystem->getParams();
params.cursorPos = make_float3(cursorPosLag.x, cursorPosLag.y, cursorPosLag.z);
psystem->step(timestep);
float4 *pos = NULL, *vel = NULL;
psystem->dumpBin(&pos, &vel);
sdkDumpBin(pos, numParticles * sizeof(float4), "smokeParticles_pos.bin");
sdkDumpBin(vel, numParticles * sizeof(float4), "smokeParticles_vel.bin");
if (!sdkCompareBin2BinFloat("smokeParticles_pos.bin",
sRefBin[0],
numParticles * sizeof(float4),
MAX_EPSILON_ERROR,
THRESHOLD,
argv[0])) {
g_TotalErrors++;
}
if (!sdkCompareBin2BinFloat("smokeParticles_vel.bin",
sRefBin[1],
numParticles * sizeof(float4),
MAX_EPSILON_ERROR,
THRESHOLD,
argv[0])) {
g_TotalErrors++;
}
delete psystem;
}
else {
// Normal smokeParticles rendering path
// 1st initialize OpenGL context, so we can properly set the GL for CUDA.
// This is needed to achieve optimal performance with OpenGL/CUDA interop.
initGL(&argc, argv);
findCudaDevice(argc, (const char **)argv);
// This is the normal code path for SmokeParticles
initParticles(numParticles, true, true);
initParams();
initMenus();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutKeyboardFunc(key);
glutKeyboardUpFunc(keyUp);
glutSpecialFunc(special);
glutIdleFunc(idle);
glutMainLoop();
}
exit(g_TotalErrors > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}

View File

@@ -0,0 +1,51 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PARTICLES_KERNEL_H
#define PARTICLES_KERNEL_H
#include "vector_types.h"
typedef unsigned int uint;
struct SimParams
{
float3 gravity;
float globalDamping;
float noiseFreq;
float noiseAmp;
float3 cursorPos;
float time;
float3 noiseSpeed;
};
struct float4x4
{
float m[16];
};
#endif

View File

@@ -0,0 +1,124 @@
/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* CUDA Device code for particle simulation.
*/
#ifndef _PARTICLES_KERNEL_H_
#define _PARTICLES_KERNEL_H_
#include "helper_math.h"
#include "math_constants.h"
#include "particles_kernel.cuh"
#include "thrust/device_ptr.h"
#include "thrust/for_each.h"
#include "thrust/iterator/zip_iterator.h"
#include "thrust/sort.h"
// for cuda::std::get
#include <cuda/std/utility>
cudaTextureObject_t noiseTex;
// simulation parameters
__constant__ SimParams cudaParams;
// look up in 3D noise texture
__device__ float3 noise3D(float3 p, cudaTextureObject_t noiseTex)
{
float4 n = tex3D<float4>(noiseTex, p.x, p.y, p.z);
return make_float3(n.x, n.y, n.z);
}
// integrate particle attributes
struct integrate_functor
{
float deltaTime;
cudaTextureObject_t noiseTex;
__host__ __device__ integrate_functor(float delta_time, cudaTextureObject_t noise_Tex)
: deltaTime(delta_time)
, noiseTex(noise_Tex)
{
}
template <typename Tuple> __device__ void operator()(Tuple t)
{
volatile float4 posData = cuda::std::get<2>(t);
volatile float4 velData = cuda::std::get<3>(t);
float3 pos = make_float3(posData.x, posData.y, posData.z);
float3 vel = make_float3(velData.x, velData.y, velData.z);
// update particle age
float age = posData.w;
float lifetime = velData.w;
if (age < lifetime) {
age += deltaTime;
}
else {
age = lifetime;
}
// apply accelerations
vel += cudaParams.gravity * deltaTime;
// apply procedural noise
float3 noise = noise3D(pos * cudaParams.noiseFreq + cudaParams.time * cudaParams.noiseSpeed, noiseTex);
vel += noise * cudaParams.noiseAmp;
// new position = old position + velocity * deltaTime
pos += vel * deltaTime;
vel *= cudaParams.globalDamping;
// store new position and velocity
cuda::std::get<0>(t) = make_float4(pos, age);
cuda::std::get<1>(t) = make_float4(vel, velData.w);
}
};
struct calcDepth_functor
{
float3 sortVector;
__host__ __device__ calcDepth_functor(float3 sort_vector)
: sortVector(sort_vector)
{
}
template <typename Tuple> __host__ __device__ void operator()(Tuple t)
{
volatile float4 p = cuda::std::get<0>(t);
float key = -dot(make_float3(p.x, p.y, p.z),
sortVector); // project onto sort vector
cuda::std::get<1>(t) = key;
}
};
#endif

View File

@@ -0,0 +1,117 @@
/*
* Copyright 1993-2015 NVIDIA Corporation. All rights reserved.
*
* Please refer to the NVIDIA end user license agreement (EULA) associated
* with this source code for terms and conditions that govern your use of
* this software. Any use, reproduction, disclosure, or distribution of
* this software and related documentation outside the terms of the EULA
* is strictly prohibited.
*
*/
/*
Copyright (c) 2005,
Aaron Lefohn (lefohn@cs.ucdavis.edu)
Adam Moerschell (atmoerschell@ucdavis.edu)
All rights reserved.
This software is licensed under the BSD open-source license. See
http://www.opensource.org/licenses/bsd-license.php for more detail.
*************************************************************
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of the University of California, Davis nor the names of
the contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#define HELPERGL_EXTERN_GL_FUNC_IMPLEMENTATION
#include "renderbuffer.h"
#include <helper_gl.h>
#include <iostream>
using namespace std;
Renderbuffer::Renderbuffer()
: m_bufId(_CreateBufferId())
{
}
Renderbuffer::Renderbuffer(GLenum internalFormat, int width, int height)
: m_bufId(_CreateBufferId())
{
Set(internalFormat, width, height);
}
Renderbuffer::~Renderbuffer() { glDeleteRenderbuffersEXT(1, &m_bufId); }
void Renderbuffer::Bind() { glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_bufId); }
void Renderbuffer::Unbind() { glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); }
void Renderbuffer::Set(GLenum internalFormat, int width, int height)
{
int maxSize = Renderbuffer::GetMaxSize();
if (width > maxSize || height > maxSize) {
cerr << "Renderbuffer::Renderbuffer() ERROR:\n\t"
<< "Size too big (" << width << ", " << height << ")\n";
return;
}
// Guarded bind
GLint savedId = 0;
glGetIntegerv(GL_RENDERBUFFER_BINDING_EXT, &savedId);
if (savedId != (GLint)m_bufId) {
Bind();
}
// Allocate memory for renderBuffer
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalFormat, width, height);
// Guarded unbind
if (savedId != (GLint)m_bufId) {
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, savedId);
}
}
GLuint Renderbuffer::GetId() const { return m_bufId; }
GLint Renderbuffer::GetMaxSize()
{
GLint maxAttach = 0;
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &maxAttach);
return maxAttach;
}
GLuint Renderbuffer::_CreateBufferId()
{
GLuint id = 0;
glGenRenderbuffersEXT(1, &id);
return id;
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright 1993-2015 NVIDIA Corporation. All rights reserved.
*
* Please refer to the NVIDIA end user license agreement (EULA) associated
* with this source code for terms and conditions that govern your use of
* this software. Any use, reproduction, disclosure, or distribution of
* this software and related documentation outside the terms of the EULA
* is strictly prohibited.
*
*/
/*
Copyright (c) 2005,
Aaron Lefohn (lefohn@cs.ucdavis.edu)
Adam Moerschell (atmoerschell@ucdavis.edu)
All rights reserved.
This software is licensed under the BSD open-source license. See
http://www.opensource.org/licenses/bsd-license.php for more detail.
*************************************************************
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of the University of California, Davis nor the names of
the contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#ifndef UCDAVIS_RENDER_BUFFER_H
#define UCDAVIS_RENDER_BUFFER_H
#include "framebufferObject.h"
/*!
Renderbuffer Class. This class encapsulates the Renderbuffer OpenGL
object described in the FramebufferObject (FBO) OpenGL spec.
See the official spec at:
http://oss.sgi.com/projects/ogl-sample/registry/EXT/framebuffer_object.txt
for complete details.
A "Renderbuffer" is a chunk of GPU memory used by FramebufferObjects to
represent "traditional" framebuffer memory (depth, stencil, and color buffers).
By "traditional," we mean that the memory cannot be bound as a texture.
With respect to GPU shaders, Renderbuffer memory is "write-only." Framebuffer
operations such as alpha blending, depth test, alpha test, stencil test, etc.
read from this memory in post-fragment-shader (ROP) operations.
The most common use of Renderbuffers is to create depth and stencil buffers.
Note that as of 7/1/05, NVIDIA drivers to do not support stencil Renderbuffers.
Usage Notes:
1) "internalFormat" can be any of the following:
Valid OpenGL internal formats beginning with:
RGB, RGBA, DEPTH_COMPONENT
or a stencil buffer format (not currently supported
in NVIDIA drivers as of 7/1/05).
STENCIL_INDEX1_EXT
STENCIL_INDEX4_EXT
STENCIL_INDEX8_EXT
STENCIL_INDEX16_EXT
*/
class Renderbuffer
{
public:
/// Ctors/Dtors
Renderbuffer();
Renderbuffer(GLenum internalFormat, int width, int height);
~Renderbuffer();
void Bind();
void Unbind();
void Set(GLenum internalFormat, int width, int height);
GLuint GetId() const;
static GLint GetMaxSize();
private:
GLuint m_bufId;
static GLuint _CreateBufferId();
};
#endif