使用Ray Matching实现云彩渲染

  这里先贴一个我实现的云彩效果,使用Direct3D11,并在pixel shader中使用RayMatching方法实现的,感兴趣的可以下载源代码查看,绘制效果如下:
clouds-render

实现过程主要使用perlin noise来进行云彩建模,然后根据云层厚度和高度决定云彩的颜色,最后使用ray matching算法以及体渲染原理进行颜色的累积。
关于perlin noise的简单介绍可以参考这里;

###云彩绘制部分
由于代码中有很多与云彩绘制无关的部分,所以我这里记录以下云彩绘制部分代码的介绍:
1. 代码实现主要在pixel shaderGet3DCloudColor函数里,大致为:

float3 Get3DCloudColor(float2 xy)
{
    return cloudColor;
}

xy为屏幕坐标,xy中的y要提前进行归一化,范围为(-1,1),主要是一般屏幕的宽度都大于高度;
该函数返回屏幕某一像素的颜色;
2. 首先设置相机矩阵

float3 eye = float3(0, 0, -30);
float3 at = float3(0, 20, 0);
matrix View = SetViewMatrix(eye, at);

我这里将相机矩阵计算放在了SetViewMatrix函数里,该函数使用eye、at以及默认沿Y轴向上的up向量来计算。
3. 沿像素投射光线

//set ray
Ray ray;
ray.dir = normalize(float3(xy, 1.732));     //y方向fov为60度
ray.pos = float3(0, 0, 0);

ray.dir = mul(float4(ray.dir, 0.0), View).xyz;      //变换射线位置和方向
ray.pos = mul(float4(ray.pos, 1.0), View).xyz;

其中Ray为包含pos、dir两个float3类型的结构体。首先构造出从原点发射的射线,然后使用View矩阵将射线变换至相机位置。
4. 设置云层高度,并测试射线是否与云层相交

//cloud height range: (6, 12)
//采用相交测试,对未相交像素,返回背景色
float2 range = float2(6, 12);
float len = 0;
float3 bgcolor = GetSkyColor(xy, ray.dir, sunDir);
if (abs(ray.dir.y) <= DELTA)        //与云层平行
{
    return bgcolor;
}

len = (range.x - ray.pos.y) / ray.dir.y;
if (len < 0)    //视线远离云层
{
    return bgcolor;
}

ray.pos += len *ray.dir;	//将射线移动到云层底部,后面在云层内进行步进运算

云层高度为(6,12),若射线远离云层或与云层平行,则返回背景色,即太阳与蓝天的颜色。
5. 使用步进方法进行云层颜色的累积

//在云层内进行步进
float3 sumCol = (float3)0;
float2 mid_width = float2((range.x + range.y) / 2, range.y - range.x);
float t = 1.0;
float transmittance = 1.0f;
for (int i = 0; i < 16; i++)
{
	if (ray.pos.y > range.y )	//穿出云层
	{
		break;
	}
	else		//步进累计颜色
	{
		ray.pos += ray.dir*t;
		float density = GetLocalDensity(ray.pos, mid_width);
		float3 localCol = GetLocalLight(ray.pos, density, mid_width, sunCol) * (1.0f - exp(-density*t));

		sumCol += localCol * transmittance;
		transmittance *= exp(-t*density);

	}
}
sumCol += bgcolor*transmittance;
  • 由于是从空气到云层密度是有一个渐变的过程的,因此我们对云层的密度进行处理,密度的计算过程在函数GetLocalDensity(ray.pos, mid_width);中,大体思路就是采用云层的高度对噪音模型采样的密度进行一个线性控制。
  • 整个循环过程就是光线在云层内步进的过程,而云层内的光照计算则采用了简单的体渲染技术,即步进的指数衰减。
  • 函数GetLocalLight(ray.pos, density, mid_width, sunCol)内部则计算步进点处当地的光照计算。

体渲染的具体技术可以参考这里,这是寒霜引擎将要引人的体渲染技术,里面下载连接里有很多介绍。

6. 将根据距离进行雾效处理

float3 fogColor = float3(1.0, 1.00, 0.94);
float factor = clamp(0, 1, (distance(eye, ray.pos) - 64.0)/ 100.0);
sumCol = lerp(sumCol, fogColor, factor);

这里使用简单的线性雾效进行处理,最后形成最终的像素颜色。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C++中实现Shape Matching算法可以使用图像处理库(如OpenCV)来进行图像处理和特征提取。下面是一个简单的C++示例代码,演示了如何使用OpenCV库实现Shape Matching算法: ```cpp #include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; int main() { // 读取待匹配和参考图像 Mat queryImage = imread("query.jpg", IMREAD_GRAYSCALE); // 待匹配图像 Mat referenceImage = imread("reference.jpg", IMREAD_GRAYSCALE); // 参考图像 // 提取待匹配和参考图像的特征描述符 vector<KeyPoint> queryKeypoints, referenceKeypoints; Mat queryDescriptors, referenceDescriptors; Ptr<Feature2D> detector = ORB::create(); // 使用ORB特征描述符 detector->detectAndCompute(queryImage, noArray(), queryKeypoints, queryDescriptors); detector->detectAndCompute(referenceImage, noArray(), referenceKeypoints, referenceDescriptors); // 使用FLANN匹配器进行特征匹配 FlannBasedMatcher matcher; vector<vector<DMatch>> matches; matcher.knnMatch(queryDescriptors, referenceDescriptors, matches, 2); // 选择最佳匹配 vector<DMatch> goodMatches; for (size_t i = 0; i < matches.size(); i++) { if (matches[i][0].distance < 0.75 * matches[i][1].distance) { goodMatches.push_back(matches[i][0]); } } // 输出匹配结果 Mat matchImage; drawMatches(queryImage, queryKeypoints, referenceImage, referenceKeypoints, goodMatches, matchImage); imshow("Matches", matchImage); waitKey(0); return 0; } ``` 上述代码使用OpenCV库中的ORB特征描述符和FLANN匹配器来实现Shape Matching算法。首先,代码读取待匹配和参考图像,并使用ORB特征描述符提取它们的特征描述符。然后,使用FLANN匹配器对特征描述符进行匹配,并选择最佳匹配结果。最后,代码绘制出匹配结果并显示。 请注意,这只是一个简单的示例,实际的Shape Matching算法可能需要更多的处理步骤和参数调整。此外,具体的实现细节可能因应用需求和算法选择而有所不同。 希望这个示例能够帮助你开始在C++中实现Shape Matching算法!如有任何进一步的问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值