OpenGl法向量计算

一、简介

顶点法向量的作用: 对渲染时光照的影响,造成不同的反射角度。

法向量:垂直于顶点所在的最小平面的单位向量,这个值是近似值。

二、目的

如何计算顶点法向量?

三、我的总结

网上对于法向量的计算方法有多种:1.取顶点周围三角面片的法向量的平均值 2.加权算法 3.....等,大同小异,无法就是取个近似值,每种方法在不同的情景中,所得的近似值各有千秋。

本文参考外文代码,给出实现的demo。

先参看下图;


代码是个加载地形的类:

class Terrain{
	private:
		int w;
		int l;
		float** hs;
		Vec3f** normals;
		bool computedNormals;
	public:
		Terrain(int w2, int l2){
			w = w2;
			l = l2;

			hs = new float*[l];
			for (int i = 0; i < l; i++){
				hs[i] = new float[w];
			}

			normals = new Vec3f*[l];
			for (int i = 0; i < l; i++){
				normals[i] = new Vec3f[w];
			}

			computedNormals = false;
		}

		~Terrain(){
			for (int i = 0; i < l; i++){
				delete [] hs[i];
			}
			delete[] hs;

			for(int i = 0; i < l; i++){
				delete[] normals[i];
			}
			delete[] normals;
		}

		int width(){
			return w;
		}

		int length(){
			return l;
		}

		void setHeight(int x, int z, float y){
			hs[z][x] = y;
			computedNormals = false;
		}

		float getHeight(int x, int z){
			return hs[z][x];
		}

		void computeNormals(){
			if(computedNormals){
				return;
			}

			Vec3f** normals2 = new Vec3f*[l];
			for (int i = 0; i < l; i++) {
				normals2[i] = new Vec3f[w];
			}

			for(int z = 0; z < l; z++){
				for (int x = 0; x < w; x++){
					Vec3f sum(0.0f, 0.0f, 0.0f);

					Vec3f out;
					if (z > 0)
					{
						out = Vec3f(0.0f, hs[z-1][x] - hs[z][x], -1.0f);
					}

					Vec3f in;
					if(z < l - 1){
						in = Vec3f(0.0f, hs[z + 1][x] - hs[z][x], 1.0f);
					}

					Vec3f left;
					if (x > 0){
						left = Vec3f(-1.0f, hs[z][x-1] - hs[z][x], 0.0f);
					}

					Vec3f right;
					if (x < w - 1){
						right = Vec3f(1.0f, hs[z][x+1] - hs[z][x], 0.0f);
					}

					if (x > 0 && z > 0){
						sum += out.cross(left).normalize();
					}

					if (x > 0 && z < l - 1){
						sum += left.cross(in).normalize();
					}

					if(x < w - 1 && z < l - 1){
						sum += in.cross(right).normalize();
					}

					if (x < w - 1 && z > 0){
						sum += right.cross(out).normalize();
					}

					normals2[z][x] = sum;
				}
			}
			const float FALLOUT_RATIO = 0.5f;
			for (int z = 0; z < l; z++)	{
				for(int x = 0; x < w; x++){
					Vec3f sum = normals2[z][x];

					if(x > 0){
						sum += normals2[z][x-1] * FALLOUT_RATIO;
					}

					if(x < w - 1){
						sum += normals2[z][x+1] * FALLOUT_RATIO;
					}

					if(z > 0){
						sum += normals2[z-1][x] * FALLOUT_RATIO;
					}

					if(z < l -1){
						sum += normals2[z+1][x] * FALLOUT_RATIO;
					}

					if(sum.magnitude() == 0){
						sum = Vec3f(0.0f, 1.0f, 0.0f);
					}

					normals[z][x] = sum;
				}
			}

			for (int i = 0; i < l; i++)
			{
				delete[] normals2[i];
			}
			delete[] normals2;

			computedNormals = true;
		}

		Vec3f getNormal(int x, int z){
			if (!computedNormals){
				computeNormals();
			}

			return normals[z][x];
		}
};


函数void computeNormals()便是计算法向量方法。



                
### Python 实现点云法向量计算 在Python中,可以利用`open3d`库来进行点云的法向量计算。此过程通常涉及构建KD树以提高搜索效率并找到每个点的最近邻点集,进而估算各点处的法向量。 #### 使用Open3D 库进行点云法向量计算 为了实现这一目标,首先需要安装必要的依赖项: ```bash pip install open3d numpy ``` 接着可以通过以下代码片段展示具体操作流程: ```python import open3d as o3d import numpy as np # 加载点云文件(此处假设为.ply格式) pcd = o3d.io.read_point_cloud("path_to_your_pcd_file.ply") # 可视化原始点云 o3d.visualization.draw_geometries([pcd]) # 设置参数用于估计法向量 radius_normal = 0.1 # 定义搜索半径 max_nn = 30 # 设定最大近邻数目 print("Estimating point cloud normals...") pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=radius_normal, max_nn=max_nn)) # 打印前几个顶点对应的法线方向 for i in range(min(len(pcd.normals), 5)): print(f'Point {i} normal:', np.asarray(pcd.normals)[i]) # 更新渲染选项以便显示法线箭头 vis_opt = o3d.visualization.Visualizer() vis_opt.create_window() opt = vis_opt.get_render_option() opt.show_coordinate_frame = True opt.point_show_normal = True vis_opt.add_geometry(pcd) vis_opt.run() vis_opt.destroy_window() ``` 上述脚本展示了如何加载外部点云数据、设置合理的参数范围来寻找局部区域内的邻居点,并最终调用内置函数完成法向量的估计工作[^1]。 此外,在某些情况下可能还需要考虑更复杂的场景,比如当面对噪声较大的点云时,则可尝试采用基于最小二乘拟合法或其他高级技术进一步提升精度[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值