Triton 的高级功能应用专题
与你自己的资源管理器集成
我们可以派生出你自己的Triton::ResourceLoader类,以钩住你自己的资源管理器。实现你自己的Triton::ResourceLoader::LoadResource()和Triton::ResourceLoader::FreeResource()方法,从你可能拥有的任何资源数据库中获取Triton的着色器和图形资源,它们位于SDK的资源目录中。然后,将你派生的Triton::ResourceLoader传递给Triton::Environment::Initialize(),它将被调用,而不是我们默认的那个。
然而,我们的资源目录中的DLL需要特别注意。你需要在磁盘上保持资源目录的vc9和/或vc10子目录,并继续向Triton::ResourceLoader构造函数传递资源目录的有效路径,这样Triton就知道在哪里可以找到我们的FFT DLL。如果不这样做,将迫使Triton退回到只用CPU的FFT转换上,这将大大损害性能。
你还需要确保位于resources/dll目录下的第三方DLL分布在Triton能够加载它们的地方,作为DLL搜索路径的一部分。我们对Triton::ResourceLoader::SetResourceDirPath()的默认实现调用Windows函数SetDllDirectory来将这个目录添加到DLL搜索路径中。你要模仿这种行为,或者将这些DLL与你的应用程序的可执行文件或工作目录一起重新分配,这样Triton在加载时就能找到它们。
与你自己的内存管理器集成
如果你想钩住你自己的内存管理器,而不是使用我们的内存管理器(它只是基于Windows的HeapAlloc()和HeapFree()函数),这是可以做到的。
然后,在调用任何其他Triton方法或实例化任何Triton对象之前,通过静态的Triton::Allocator::SetAllocator()方法将其传入。
由于Triton是跨DLL边界操作的,你要注意使用一个一致的堆,就像我们一样。授权用户可以访问Triton的全部源代码,在创建你自己的Triton::Allocator之前,你可能会发现检查我们自己的实现是很有意义的。
我们注意捕捉Triton中new、delete、malloc和free的每一次使用,以及与STL对象分配的任何内存的挂钩。然而,系统函数和第三方库(如CUDA、OpenCL和FFTSS)可能会分配内存,这不在我们的控制之内。
在OpenGL 4.5中使用无约束图形
Triton有能力利用 "无束缚图形",它使用本地GPU地址,而不是必须由驱动程序查找和验证的资源句柄。这提供了一些性能上的好处,也让我们更接近于为Vulkan准备的架构。
这个功能默认是禁用的;Resources/Triton.config文件中的相关设置是:
use-gl-bindless-textures
use-gl-bindless-uniform-buffer-objects
use-gl-bindless-vertex-buffers
如果设置为 "是",这些设置将分别启用无绑定纹理、UBO和VBO。它们的使用需要使用OPENGL_4_5或更新的渲染器类型和Triton(在Environment::Initialize()中指定),而无束缚的UBO和VBO依赖于NVidia特定的扩展,这些扩展可能在你的目标系统中不存在。这就是为什么它们在默认情况下被禁用。然而,如果你控制了你要部署软件的系统的配置,并且它们与NVidia的无绑定扩展兼容,你可以在你的部署中测试启用这些选项。无约束纹理依赖于GL_ARB_bindless_texture扩展的可用性;无约束UBO依赖于GL_ARB_uniform_buffer_object、GL_NV_vertex_buffer_unified_memory和GL_NV_shader_buffer_load;而无约束VBO则依赖于GL_NV_vertex_buffer_unified_memory和GL_NV_shader_buffer_load。
在水中添加你自己的效果
你可以在资源文件夹内找到Triton的纯文本着色器;.glsl文件是用于OpenGL的,.fx是用于DirectX9和DirectX11的效果文件。你可以自由地对这些着色器进行修改,以定制你的应用程序,或与你的图形引擎更紧密地集成。例如,你可以引入对额外光源的支持。
如果你正在开发对Triton着色器的修改,最好在resources/Triton.config中启用enable-debug-messages设置,这样你就可以看到着色器中的任何编译错误。任何错误信息都会出现在你的调试器的输出日志中。
你的修改可能需要额外的参数被传递到着色器中。为了实现这一点,我们提供了Triton::Ocean::GetShaderObject()方法。根据所使用的渲染器,这将返回给你一个GLhandle、一个ID3DXEffect指针或一个ID3DX11Effect指针,你可以用它来传入你自己的统一参数--例如,你自己的纹理或矩阵,用于在与水结合时进行转换。GetShaderObject()接收一个参数,表示你想修改哪个着色器。对于海洋表面,请确保将你的制服应用于WATER_SURFACE和WATER_SURFACE_PATCH程序。这些着色器程序分别在使用投影网格和传统网格时绘制水面。
如果你想把你修改的着色器与Triton的原始着色器分开,你可以使用resources/Triton.config中的配置设置 "shader-filename-prefix "来在加载着色器文件名时添加一个前缀。例如,设置 "shader-filename-prefix = my-"将导致使用 "my-flat-fft.fx "文件而不是 "flat-ffx.fx"。
我们还提供了 "user-functions.glsl "文件,它为Triton的所有几何体的照明提供了易于使用的挂钩。参考这个文件中的注释,学习如何覆盖Triton的阴影。通过将你的自定义着色代码放在user-functions.glsl中,你可以避免直接修改我们的着色器,这使得更新Triton更加容易。这也有一个好处,就是将你的自定义着色代码放在一个地方,而不用担心为地心、平面、投影网格和传统网格渲染而扩展Triton的所有不同的单独着色器程序。
对于更深层次的整合,除了二进制许可证之外,我们还在我们的网站(www.dhorde.com)上提供完整的源代码许可证。然而,即使是二进制许可证,你也会收到着色器的源码,并能够像这里描述的那样与它们挂钩。
从源代码构建Triton
Triton Ocean SDK的授权客户将收到一个特殊的SDK安装程序,其中包括Triton的全部源代码,使您能够修改Triton以满足您自己的特殊需求。
在SDK的顶层目录中,你会发现Visual Studio 2015-2019(vc14)和2022(vc143)的解决方案文件。选择你需要的构建口味,例如 "Triton-MT-DLL "用于多线程、非调试、DLL运行时间,"Triton-MTD-DLL "用于多线程调试DLL运行时间,等等。
为了编译TritonCUDA DLL项目,如果你正在编译我们的Visual Studio 2015-2019库(-vc14解决方案和项目文件),你需要安装NVidia的CUDA工具包11.1;如果你用Visual Studio 2022解决方案文件(-vc143)进行编译,你需要安装CUDA工具包11.7。如果你是针对32位目标进行编译,请先安装CUDA工具包6.5版本,然后再安装相应的11版本。 这种复杂性是由于CUDA工具包在11.1版本后放弃了对Visual Studio 2015的支持,在6.5版本后放弃了对32位构建的支持。Linux用户可以直接安装支持你所开发的操作系统和架构的最新的CUDA工具包,并以此为目标。
CUDA工具包可从Nvidia网站免费获取,具体版本可通过其CUDA工具包存档页面获取(目前在 developer.nvidia.com/cuda-toolkit-archive)。
如果你要构建32位目标并需要CUDA支持,你首先也需要安装Visual Studio 2013(免费社区版即可)。NVidia的CUDA Toolkit 6.5(32位支持所需)不支持Visual Studio 2015或更新版本,因此我们的TritonCUDA项目在这种情况下被设置为使用Visual Studio 2013(vc12)编译器。
你会发现,除了公共的API之外,完整的SDK所包含的文档还包括对Triton所有内部类的参考。
如果你在构建Triton时遇到任何问题,请联系我们:https://www.dhorde.com/ 在线客服,电话 023-62585653,或邮箱 sales@dhorde.com。
将平面反射图与Triton结合起来
除了环境立方体地图之外,你也可以传递平面反射地图给Triton使用。这对于生成船只和地形的局部反射很有用。使用Triton::Environment::SetPlanarReflectionMap()可以达到这个效果。
传递到这个方法中的纹理参数的类型将取决于你所使用的渲染器。OpenGL用户应该传入一个代表平面反射纹理ID的GLuint。DirectX9用户应该传入一个LPDIRECT3DTEXTURE2D。DirectX11用户应该传递一个ID3D11ShaderResourceView指针。
Triton可以同时使用平面反射和环境贴图纹理。如果两者都被应用,平面反射纹理的alpha通道被用来在平面反射和环境立方体贴图的反射之间进行混合。这是一个获得 "两全其美 "的好方法,立方体贴图提供来自陡峭波浪角度的环境反射,而平面反射贴图提供直接在水面上的局部反射。
平面反射图的生成可以被认为是一个高级课题。它需要一个渲染到纹理的过程来产生一个合适的反射图。渲染到这种纹理的场景必须通过将高度值缩放为-1来镜像反射平面(这可以通过将一个缩放矩阵乘以反射相机的视图矩阵来实现)。这样的缩放会翻转缠绕方向,所以在渲染这个反射相机时,有必要改变用于背面剔除的多边形的缠绕顺序。可能还需要应用用户剪辑平面来切断通常隐藏在水面下的场景模型的碎片。所有这些主题在互联网上的各种文件中都有非常详细的描述。如果你想了解更多的信息,请在网上搜索 "Water Rendering",你一定会找到很多关于渲染镜像纹理的文档。SDK中包含的OpenSceneGraphReflection样本也可以作为参考。
与平面反射纹理一起,Triton需要传递一个纹理矩阵,用于将Triton计算的反射矢量投射到纹理坐标中。在最常见的情况下,这正是用于渲染主摄像机的View*Projection矩阵,它被缩放并转换到0...1 x 0...1纹理坐标空间。通常,你为反射图查询构建纹理矩阵时,首先将主场景的视图矩阵中的任何平移归零,然后乘以投影矩阵,再乘以一个转换矩阵,该矩阵平移为(1,1,1),缩放为(0.5,0.5,0.5),以获得纹理坐标。(由于不同的惯例,DirectX用户会希望用(1, 1, 0)来翻译,用(0.5, -0.5, 1.0)来缩放)。然而,旨在优化使用反射图空间的更复杂的方案可以采用不太直观的视图和投影矩阵。这种不寻常的TextureMatrices也可以传递给Triton::Environment::SetEnvironmentMap()。
例如,这里有一些OpenSceneGraph的代码,可以计算出适当的纹理矩阵传递给Environment::SetPlanarReflectionMap,假设纹理是从一个使用与主摄像机相同的视图和位置来翻转高度坐标的摄像机中渲染出来的:
view.setTrans( 0, 0, 0 );
_textureProjectionMatrix->set( view *
projection *
osg::Matrix::translate( 1,1,1 ) *
osg::Matrix::scale( 0.5, 0.5, 0.5 ) );
在投影纹理映射到反射纹理之前,无论你传入什么矩阵,都将应用于我们的片段着色器中的视图向量。我们的着色器都位于资源文件夹中,并在运行时进行编译,所以如果你需要修改它们以支持其他反射方案,你可以自由地这样做。
Triton确实提供了一个辅助函数来计算常用的平面反射和纹理矩阵,即Triton::Ocean::ComputeReflectionMatrices()。这个方法将给你一个4x4的矩阵来翻转你的场景,关于当地的水面,还有一个3x3的矩阵,可以用于纹理查询到你的反射纹理。下面是一些OpenGL的伪代码,说明如何使用它来创建一个有效的反射纹理图和矩阵,适合Environment::SetPlanarReflectionMap()。
// The following code renders models into a texture as part of the reflection map pass.
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// Apply the camera position and rotation.
glLoadIdentity();
glRotated(pitch, 1, 0, 0);
glRotated(yaw, 0, 1, 0);
glTranslated(-camX, -camY, -camZ);
// Pass the camera info to Triton
double mv[16], proj[16];
glGetDoublev(GL_MODELVIEW_MATRIX, mv);
glGetDoublev(GL_PROJECTION_MATRIX, proj);
tritonEnvironment->SetCameraMatrix(mv);
tritonEnvironment->SetProjectionMatrix(proj);
// Ask Triton for reasonable matrices to use
Triton::Matrix3 reflectTexMatrix;
Triton::Matrix4 reflectionMatrix;
if (tritonOcean->ComputeReflectionMatrices(reflectionMatrix, reflectTexMatrix))
{
// Flip everything about the water surface
glMultMatrixf(reflectionMatrix.ToFloatArray());
// Activate a render to texture target for your reflection texture
// Setting up and using an FBO is a lot of code so we're not showing
// that here, contact support@sundog-soft.com if you need help.
frameBufferObject->SetupRenderPass();
// Flip the back face culling winding order, since we flipped everything
glFrontFace(GL_CW);
// Set up a user clipping plane to prevent reflection geometry under water
// There are also tricks for doing this by manipulating the near clip plane
// of your projection matrix if you want to look that up.
double planeEq[4] = {0, 1, 0, tritonEnvironment->GetSeaLevel()};
glClipPlane(GL_CLIP_PLANE0, planeEq);
glEnable(GL_CLIP_PLANE0);
// Draw anything you want reflected in the water. Note this is additive to
// any environmental cube map reflections you may have, so you can handle
// sky reflections separately and less frequently.
DrawModels(true);
glDisable(GL_CLIP_PLANE0);
// Close out your render texture and restore things the way they were
frameBufferObject->EndRenderPass();
glFrontFace(GL_CCW);
// Give it to Triton
tritonEnvironment->SetPlanarReflectionMap((Triton::TextureHandle)(frameBufferObject->GetTexture()),
reflectTexMatrix);
// Blend the reflection with other reflections 60%.
tritonOcean->SetPlanarReflectionBlend(0.6f);
}
glPopMatrix();
请注意,我们还使用了Triton::Ocean::SetPlanarReflectionBlend()来控制最终输出中的平面反射的强度。你可以用它来在平静的水面上有更强的反射,或者在波浪高度增加时或在禁用反射通道之前完全淡化反射。
限制计算资源的使用
Triton通过使用它能找到的任何并行计算资源来实现其性能,包括你的GPU和多核CPU。默认情况下,它通常会最大限度地使用您的GPU和CPU,以尽可能快地渲染水。在实践中,这意味着每一帧都有更多的时间来渲染场景中的其他物体。
一些用户发现这种行为是不可取的,因为它使你的计算机如此努力地运行而产生的电力消耗和热量,或者它可能干扰了你的应用程序中其他并行进程的调度。我们允许你控制Triton如何积极地使用你的计算资源以减少这些问题。
如果你需要保护CPU资源,Triton::Environment::EnableOpenMP()可以用来阻止使用OpenMP将密集的循环分割到多个内核。这将限制Triton使用它所调用的内核,除非Triton退回到基于CPU的FFT计算。
如果你需要保护GPU资源,你可能想禁用Triton对CUDA、OpenCL和DirectX11计算着色器的使用,以迫使所有FFT计算在CPU上进行。resources/triton.config中的配置设置disable-cuda、disable-opencl和disable-compute-shader可以用来实现这一目的。
在Triton中使用线性色彩空间
如果你的应用程序在内部以线性色彩空间渲染其场景,你将需要告诉Triton修正其着色器,以消除伽马修正的影响。
Triton::Ocean::SetLinearColorSpace()方法可用于此目的。如果设置为 "true",海洋表面的颜色将被提高到2.2的倍数(这就取消了2.2的伽马校正);如果 "false",海洋表面的颜色将保持不变。
渝公网安备50010702505508