Fluid Engine Development 看了很久,应该要做 Demo 来演示算法了
项目 level_set_liquid_sim
里面有运行 Level Set 的模拟,输出三角面的示例代码
项目 hybrid_liquid_sim
里面有运行 PIC FLIP APIC 的模拟,但是只输出位置信息的示例代码,比如他保存成 xyz 格式
项目 particles2obj
可以把位置信息文件 xyz 转成三角面文件 obj
项目 sandbox
提供了自己创建求解器的模板,但是现在暂时应该用不到,现在想输出他的求解器的图像
然后他这个 Marching Cube 的算法里面写了对于边界的处理,原仓库的代码里面特意设置了不创建底部的面的标志,我不知道为什么要这么做
void triangulateAndSave(const ScalarGrid3Ptr& sdf, const std::string& rootDir,
int frameCnt) {
TriangleMesh3 mesh;
// int flag = kDirectionAll & ~kDirectionDown;
int flag = kDirectionAll;
marchingCubes(sdf->constDataAccessor(), sdf->gridSpacing(),
sdf->dataOrigin(), &mesh, 0.0, flag);
saveTriangleMesh(mesh, rootDir, frameCnt);
}
用 Blender 渲染的话,为了做光线追踪,液体网格应该封闭才能达成正确的光线反射,现在这个不创建底面直接就没有那个正确的效果了
一开始我还不知道有这个标记,于是等那个溃坝的模拟,一帧五分钟,等了五十帧,之后才开始做,这个时候才开始发现模型没有底面……浪费了很多时间
Blender 渲染的时候记得要在 Preference-System-Cycles Render Device 选 CUDA,明显渲染会快很多,可能十多倍都有可能
然后记得构建 VS 工程要构建成 Release,开启了代码优化,计算速度会快很多,两三倍吧
因为 PIC FLIP 那边的没有保存三角面的,所以简单拼接一下就可以了
在 example 里新建一个文件夹,里面是新项目的 main.cpp 和 CMakeList.txt。这个项目我命名为 comparsion
项目的 CMakeList
#
# Copyright (c) 2018 Doyub Kim
#
# I am making my contributions/submissions to this project solely in my personal
# capacity and am not conveying any rights to any intellectual property of any
# third parties.
#
# Target name
set(target comparsion)
# Includes
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
# Sources
file(GLOB sources
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# Build executable
add_executable(${target}
${sources})
# Project options
set_target_properties(${target}
PROPERTIES
${DEFAULT_PROJECT_OPTIONS}
)
# Compile options
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_definitions(-D_USE_MATH_DEFINES) # for M_PI
endif ()
target_compile_options(${target}
PRIVATE
PUBLIC
${DEFAULT_COMPILE_OPTIONS}
INTERFACE
)
# Link libraries
target_link_libraries(${target}
PRIVATE
${DEFAULT_LINKER_OPTIONS}
jet
pystring)
要在根目录的 CMakeList 里加上新项目
add_subdirectory(src/examples/comparsion)
main.cpp
// Copyright (c) 2018 Doyub Kim
//
// I am making my contributions/submissions to this project solely in my
// personal capacity and am not conveying any rights to any intellectual
// property of any third parties.
#include <jet/jet.h>
#include <pystring/pystring.h>
#ifdef JET_WINDOWS
#include <direct.h>
#else
#include <sys/stat.h>
#endif
#include <example_utils/clara_utils.h>
#include <clara.hpp>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#define APP_NAME "comparsion"
using namespace jet;
void saveTriangleMesh(const TriangleMesh3& mesh, const std::string& rootDir,
int frameCnt) {
char basename[256];
snprintf(basename, sizeof(basename), "frame_%06d.obj", frameCnt);
std::string filename = pystring::os::path::join(rootDir, basename);
std::ofstream file(filename.c_str());
if (file) {
printf("Writing %s...\n", filename.c_str());
mesh.writeObj(&file);
file.close();
}
}
void triangulateAndSave(const ScalarGrid3Ptr& sdf, const std::string& rootDir,
int frameCnt) {
TriangleMesh3 mesh;
// int flag = kDirectionAll & ~kDirectionDown;
int flag = kDirectionAll;
marchingCubes(sdf->constDataAccessor(), sdf->gridSpacing(),
sdf->dataOrigin(), &mesh, 0.0, flag);
saveTriangleMesh(mesh, rootDir, frameCnt);
}
void printInfo(const PicSolver3Ptr& solver) {
auto grids = solver->gridSystemData();
Size3 resolution = grids->resolution();
BoundingBox3D domain = grids->boundingBox();
Vector3D gridSpacing = grids->gridSpacing();
printf("Resolution: %zu x %zu x %zu\n", resolution.x, resolution.y,
resolution.z);
printf("Domain: [%f, %f, %f] x [%f, %f, %f]\n", domain.lowerCorner.x,
domain.lowerCorner.y, domain.lowerCorner.z, domain.upperCorner.x,
domain.upperCorner.y, domain.upperCorner.z);
printf("Grid spacing: [%f, %f, %f]\n", gridSpacing.x, gridSpacing.y,
gridSpacing.z);
}
void runSimulation(const std::string& rootDir, const PicSolver3Ptr& solver,
int numberOfFrames, double fps) {
auto sdf = solver->signedDistanceField();
for (Frame frame(0, 1.0 / fps); frame.index < numberOfFrames; ++frame) {
solver->update(frame);
triangulateAndSave(sdf, rootDir, frame.index);
}
}
// Water-drop example (FLIP)
void runExample1(const std::string& rootDir, size_t resolutionX,
int numberOfFrames, double fps) {
// Build solver
auto solver =
FlipSolver3::builder()
.withResolution({
resolutionX, 2 * resolutionX, resolutionX})
.withDomainSizeX(1.0)
.makeShared();
auto grids = solver->gridSystemData();
auto particles = solver->particleSystemData();
Vector3D gridSpacing = grids->gridSpacing();
double dx = gridSpacing.x;
BoundingBox3D domain = grids->boundingBox();
// Build emitter
auto plane = Plane3::builder()
.withNormal({
0, 1, 0})
.withPoint({
0, 0.25 * domain.height(), 0})
.makeShared();
auto sphere = Sphere3::builder()