//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //============================================================================ #include #include #include #include #include #include #include namespace vtkm { namespace rendering { namespace raytracing { namespace detail { template class SurfaceShade { public: class Shade : public vtkm::worklet::WorkletMapField { private: vtkm::Vec3f_32 LightPosition; vtkm::Vec3f_32 LightAmbient{ .5f, .5f, .5f }; vtkm::Vec3f_32 LightDiffuse{ .7f, .7f, .7f }; vtkm::Vec3f_32 LightSpecular{ .7f, .7f, .7f }; vtkm::Float32 SpecularExponent = 20.f; vtkm::Vec3f_32 CameraPosition; vtkm::Vec3f_32 LookAt; Precision MissScalar; public: VTKM_CONT Shade(const vtkm::Vec3f_32& lightPosition, const vtkm::Vec3f_32& cameraPosition, const vtkm::Vec3f_32& lookAt, const Precision missScalar) : LightPosition(lightPosition) , CameraPosition(cameraPosition) , LookAt(lookAt) , MissScalar(missScalar) { } using ControlSignature = void(FieldIn, FieldIn, FieldIn, FieldOut); using ExecutionSignature = void(_1, _2, _3, _4); VTKM_EXEC void operator()(const vtkm::Id& hitIdx, const vtkm::Vec& normal, const vtkm::Vec& intersection, Precision& output) const { if (hitIdx < 0) { output = MissScalar; } vtkm::Vec lightDir = LightPosition - intersection; vtkm::Vec viewDir = CameraPosition - LookAt; vtkm::Normalize(lightDir); vtkm::Normalize(viewDir); //Diffuse lighting Precision cosTheta = vtkm::dot(normal, lightDir); //clamp tp [0,1] const Precision zero = 0.f; const Precision one = 1.f; cosTheta = vtkm::Min(vtkm::Max(cosTheta, zero), one); //Specular lighting vtkm::Vec reflect = 2.f * vtkm::dot(lightDir, normal) * normal - lightDir; vtkm::Normalize(reflect); Precision cosPhi = vtkm::dot(reflect, viewDir); Precision specularConstant = vtkm::Pow(vtkm::Max(cosPhi, zero), static_cast(SpecularExponent)); Precision shade = vtkm::Min( LightAmbient[0] + LightDiffuse[0] * cosTheta + LightSpecular[0] * specularConstant, one); output = shade; } vtkm::Vec3f_32 GetDiffuse() { return LightDiffuse; } vtkm::Vec3f_32 GetAmbient() { return LightAmbient; } vtkm::Vec3f_32 GetSpecular() { return LightSpecular; } vtkm::Float32 GetSpecularExponent() { return SpecularExponent; } void SetDiffuse(vtkm::Vec3f_32 newDiffuse) { LightDiffuse[0] = newDiffuse[0]; LightDiffuse[1] = newDiffuse[1]; LightDiffuse[2] = newDiffuse[2]; } void SetAmbient(vtkm::Vec3f_32 newAmbient) { LightAmbient[0] = newAmbient[0]; LightAmbient[1] = newAmbient[1]; LightAmbient[2] = newAmbient[2]; } void SetSpecular(vtkm::Vec3f_32 newSpecular) { LightSpecular[0] = newSpecular[0]; LightSpecular[1] = newSpecular[1]; LightSpecular[2] = newSpecular[2]; } void SetSpecularExponent(vtkm::Float32 newExponent) { SpecularExponent = newExponent; } }; //class Shade VTKM_CONT void run(Ray& rays, const vtkm::rendering::raytracing::Camera& camera, const Precision missScalar, vtkm::cont::ArrayHandle shadings, bool shade) { if (shade) { // TODO: support light positions vtkm::Vec3f_32 scale(2, 2, 2); vtkm::Vec3f_32 lightPosition = camera.GetPosition() + scale * camera.GetUp(); vtkm::worklet::DispatcherMapField( Shade(lightPosition, camera.GetPosition(), camera.GetLookAt(), missScalar)) .Invoke(rays.HitIdx, rays.Normal, rays.Intersection, shadings); } } }; // class SurfaceShade template class FilterDepth : public vtkm::worklet::WorkletMapField { private: Precision MissScalar; public: VTKM_CONT explicit FilterDepth(const Precision missScalar) : MissScalar(missScalar) { } typedef void ControlSignature(FieldIn, FieldInOut); typedef void ExecutionSignature(_1, _2); VTKM_EXEC void operator()(const vtkm::Id& hitIndex, Precision& scalar) const { Precision value = scalar; if (hitIndex < 0) { value = MissScalar; } scalar = value; } }; //class FilterDepth template class WriteBuffer : public vtkm::worklet::WorkletMapField { private: Precision MissScalar; public: VTKM_CONT explicit WriteBuffer(const Precision missScalar) : MissScalar(missScalar) { } typedef void ControlSignature(FieldIn, FieldIn, FieldOut); typedef void ExecutionSignature(_1, _2, _3); VTKM_EXEC void operator()(const vtkm::Id& hitIndex, const Precision& scalar, Precision& output) const { Precision value = scalar; if (hitIndex < 0) { value = MissScalar; } output = value; } }; //class WriteBuffer template class WriteDepthBuffer : public vtkm::worklet::WorkletMapField { public: typedef void ControlSignature(FieldIn, FieldOut); typedef void ExecutionSignature(_1, _2); VTKM_EXEC void operator()(const Precision& depth, Precision& output) const { output = depth; } }; //class WriteDepthBuffer } // namespace detail void ScalarRenderer::SetShapeIntersector(std::unique_ptr&& intersector) { Intersector = std::move(intersector); } void ScalarRenderer::AddField(const vtkm::cont::Field& scalarField) { const auto& ranges = scalarField.GetRange(); if (ranges.GetNumberOfValues() != 1) { throw vtkm::cont::ErrorBadValue("ScalarRenderer(AddField): field must be a scalar"); } Fields.push_back(scalarField); } void ScalarRenderer::Render(Ray& rays, vtkm::Float32 missScalar, vtkm::rendering::raytracing::Camera& cam) { RenderOnDevice(rays, missScalar, cam); } void ScalarRenderer::Render(Ray& rays, vtkm::Float64 missScalar, vtkm::rendering::raytracing::Camera& cam) { RenderOnDevice(rays, missScalar, cam); } template void ScalarRenderer::RenderOnDevice(Ray& rays, Precision missScalar, vtkm::rendering::raytracing::Camera& cam) { using Timer = vtkm::cont::Timer; Logger* logger = Logger::GetInstance(); Timer renderTimer; renderTimer.Start(); vtkm::Float64 time = 0.; logger->OpenLogEntry("scalar_renderer"); logger->AddLogData("device", GetDeviceString()); logger->AddLogData("num_rays", rays.NumRays); const size_t numFields = Fields.size(); if (numFields == 0) { throw vtkm::cont::ErrorBadValue("ScalarRenderer: no fields added"); } if (!Intersector) { throw vtkm::cont::ErrorBadValue("ScalarRenderer: intersector never set"); } Timer timer; timer.Start(); Intersector->IntersectRays(rays); time = timer.GetElapsedTime(); logger->AddLogData("intersect", time); for (size_t f = 0; f < numFields; ++f) { timer.Start(); Intersector->IntersectionData(rays, Fields[f]); time = timer.GetElapsedTime(); logger->AddLogData("intersection_data", time); AddBuffer(rays, missScalar, Fields[f].GetName()); } const vtkm::Int32 numChannels = 1; ChannelBuffer buffer(numChannels, rays.NumRays); detail::SurfaceShade surfaceShade; surfaceShade.run(rays, cam, missScalar, buffer.Buffer, true); buffer.SetName("shading"); rays.Buffers.push_back(buffer); this->Invoke(detail::FilterDepth{ missScalar }, rays.HitIdx, rays.Distance); time = renderTimer.GetElapsedTime(); logger->CloseLogEntry(time); } // RenderOnDevice template void ScalarRenderer::AddBuffer(Ray& rays, Precision missScalar, const std::string& name) { const vtkm::Int32 numChannels = 1; ChannelBuffer buffer(numChannels, rays.NumRays); this->Invoke( detail::WriteBuffer{ missScalar }, rays.HitIdx, rays.Scalar, buffer.Buffer); buffer.SetName(name); rays.Buffers.push_back(buffer); } template void ScalarRenderer::AddDepthBuffer(Ray& rays) { const vtkm::Int32 numChannels = 1; ChannelBuffer buffer(numChannels, rays.NumRays); this->Invoke(detail::WriteDepthBuffer{}, rays.Depth, buffer.Buffer); buffer.SetName("depth"); rays.Buffers.push_back(buffer); } } } } // namespace vtkm::rendering::raytracing