`

基于二次误差度量的网格简化

    博客分类:
  • Java
阅读更多
直接贴结果了:







界面:



这个还有点价值吧,麻烦下的人给贡献点分了:

http://download.csdn.net/source/2019425
or

http://www.pudn.com/downloads223/sourcecode/graph/detail1049804.html



主要的类:
package MeshSimpliefication_QuadErr;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.io.ObjectInputStream.GetField;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.TreeSet;

import javax.vecmath.Point3d;
import javax.xml.ws.FaultAction;

/**
 * 由j3d中的ObjectFile修改而来,仅用了其读文件的部分(也有修改)
 * @author Chao Liang(梁超),TsingHua university; Student ID:2009310399
 *         {@link essay1986@yahoo.com.cn}
 * 
 */
public class ObjFileSimplified_v2 {

	ArrayList<VertexComplicated> vertexList; // 顶点列表
	int vertexNumber;// 现在的顶点的真实数量
	TreeSet<EdgeWithCost> costSet;// 边折叠损耗的列表,用于排序(半自动)
	// HashSet<FaceTriangle> faces;//不需要,只在输出时用到,到时才创建
	private long time;

	public ObjFileSimplified_v2(String fileName) {
		try {
			time = System.currentTimeMillis();
			load(fileName);// 载入文件
			time = System.currentTimeMillis() - time;
			System.out.println("读文件“" + fileName + "”用时: " + ((double) time) / 1000 + " s");
		} catch (Exception e) {
			e.printStackTrace();
		}
		time = System.currentTimeMillis();
		init_Q_Costs();// 初始化损耗函数
		System.out.println("初始化用时:" + (double) (System.currentTimeMillis() - time) / 1000 + "s");
	}

	/**
	 * 读取一个顶点
	 * @param st
	 * @throws ParsingErrorException
	 */
	void readVertex(ObjectFileParser st) throws ParsingErrorException {
		Point3d p = new Point3d();
		st.getNumber();
		p.x = st.nval;
		st.getNumber();
		p.y = st.nval;
		st.getNumber();
		p.z = st.nval;
		st.skipToNextLine();
		// Add this vertex to the array
		vertexList.add(new VertexComplicated(vertexList.size(), p));
	} // End of readVertex

	/**
	 * readFace 将各个面的边分别存到对应的顶点中;修改过
	 */
	void readFace(ObjectFileParser st) throws ParsingErrorException {
		ArrayList<Integer> points = new ArrayList<Integer>();

		while (st.ttype != StreamTokenizer.TT_EOL) {
			st.getNumber();
			points.add((int) st.nval - 1);
			// st.getNumber();
			st.getToken();
			if (st.ttype == StreamTokenizer.TT_EOL)
				break;
			else
				st.pushBack();
		}

		FaceTriangle face = new FaceTriangle(points);
		// faces.add(face);//不需要,只在输出时用到,到时才创建
		// for (FaceTriangle f : faces) {
		// System.out.println(f);
		// }
		for (int i = 0; i < points.size(); i++) {// 增加边和面片,其中边以另一顶点的索引的方式存储在该顶点中
			int point1 = points.get(i);
			vertexList.get(point1).addFace(face);// 加入面片
			TreeSet<Integer> edgeInd = vertexList.get(point1).getEdgeIndexes();
			for (int j = i + 1; j < points.size(); j++) {
				int point2 = points.get(j);
				if (edgeInd.contains(point2))
					continue;
				EdgeWithCost edge = new EdgeWithCost(point1, point2);
				vertexList.get(point1).edges.add(edge);
				vertexList.get(point2).edges.add(edge);
			}
		}
		st.skipToNextLine();
	} // End of readFace

	/**
	 * readFile
	 * 
	 * Read the model data from the file.
	 */
	void readFile(ObjectFileParser st) throws ParsingErrorException {

		st.getToken();
		while (st.ttype != ObjectFileParser.TT_EOF) {
			if (st.ttype == ObjectFileParser.TT_WORD) {
				if (st.sval.equals("v")) {
					readVertex(st);
				} else if (st.sval.equals("f")) {
					readFace(st);
				} else {
				}
			}
			st.skipToNextLine();
			// Get next token
			st.getToken();
		}

		vertexNumber = vertexList.size();
	} // End of readFile

	/**
	 * The Object File is loaded from the .obj file specified by the filename.
	 * To attach the model to your scene, call getSceneGroup() on the Scene
	 * object passed back, and attach the returned BranchGroup to your scene
	 * graph. For an example, see j3d-examples/ObjLoad/ObjLoad.java.
	 */
	public void load(String filename) throws FileNotFoundException, IncorrectFormatException,
			ParsingErrorException {
		Reader reader = new BufferedReader(new FileReader(filename));
		load(reader);
	} // End of load(String)

	/**
	 * The Object File is loaded from the already opened file. To attach the
	 * model to your scene, call getSceneGroup() on the Scene object passed
	 * back, and attach the returned BranchGroup to your scene graph. For an
	 * example, see j3d-examples/ObjLoad/ObjLoad.java.
	 */
	public void load(Reader reader) throws FileNotFoundException, IncorrectFormatException,
			ParsingErrorException {
		ObjectFileParser st = new ObjectFileParser(reader);
		vertexList = new ArrayList<VertexComplicated>();
		// faces = new HashSet<FaceTriangle>();//不需要,只在输出时用到,到时才创建
		readFile(st);
	} // End of load(Reader)

	/**
	 * 打印该模型文件,输出顶点和边表
	 */
	void print() {
		int indexFrom1 = 1;
		System.out.print("points={");
		for (int i = 0; i < vertexList.size(); i++) {
			if (vertexList.get(i) != null) {
				if (i % 4 == 0) {
					System.out.println();
					System.out.print("\t");
				}
				Point3d p = vertexList.get(i).vertex;
				System.out.print("p(" + (i + indexFrom1) + ")=[ " + p.x + ", " + p.y + ", " + p.z
						+ "];   ");
			}
		}
		System.out.println();
		System.out.println("}\\end of points");

		System.out.println("edges={");// /////edges
		for (int i = 0; i < vertexList.size(); i++) {
			if (vertexList.get(i) != null) {
				System.out.print("\t" + (i + indexFrom1) + ":  ");
				// TreeSet<Integer> edges = vertexList.get(i).edgeIndexes;
				// for (int integer : edges) {
				// System.out.print((integer + indexFrom1) + ",  ");
				// }
				ArrayList<EdgeWithCost> edges = vertexList.get(i).edges;
				for (EdgeWithCost edge : edges) {
					System.out.print((edge.getOtherVertex(i) + indexFrom1) + ",  ");
				}
				System.out.println();
			}
		}
		System.out.println("}end of edges");
	}

	void init_Q_Costs() {
		for (VertexComplicated v : vertexList) {
			v.calculateQ(vertexList);// 初始化Q
		}

		// 计算各个边的边折叠损耗
		for (VertexComplicated v : vertexList) {
			ArrayList<EdgeWithCost> edges = v.edges;
			for (EdgeWithCost edge : edges) {
				if (edge.isSmallVertexIndex(v.index))
					edge.cal_v_cost(vertexList);
			}
		}
		costSet = new TreeSet<EdgeWithCost>();
		for (VertexComplicated v : vertexList) {
			ArrayList<EdgeWithCost> edges = v.edges;
			for (EdgeWithCost edge : edges) {

				if (edge.isSmallVertexIndex(v.index)) {
					boolean b = costSet.add(edge);
					// if(!b) System.out.println(edge);;
				}
			}
			// if(costSet.size()>0&&e!=null)System.out.println(costSet.contains(e));

		}

		// for (VertexComplicated v : vertexList) {
		// ArrayList<EdgeWithCost> edges = v.edges;
		// EdgeWithCost e = null;
		// for (EdgeWithCost edge : edges) {
		// if (!costSet.contains(edge))
		// System.out.println(edge);
		// else
		// System.out.println("                               正确            " +
		// edge);
		// // System.out.println(costSet.contains(edge));
		// }
		//
		// }
		// System.out.println(costSet);
		// // System.out.println();
		// System.out.println();
		// System.out.println(vertexList.get(0).edges.get(0));
		// System.out.println(costSet.contains(vertexList.get(0).edges.get(0)));
	}

	/**
	 * 边收缩操作一次,合并一条消耗最小的边的两个顶点, 快速,只更新本顶点的边折叠损耗
	 */
	void contractEdgeFast() {
		EdgeWithCost edgeLeastCost = costSet.pollFirst();// 取出消耗最小的边;已从消耗列表中删除
		VertexComplicated vSmall = vertexList.get(edgeLeastCost.getSmallVertex());// 编号小的顶点
		VertexComplicated vBig = vertexList.get(edgeLeastCost.getBigVertex());// 编号大的顶点
		vSmall.setErrorPairContraction(edgeLeastCost.cost);// step2:更新顶点的消耗
		ArrayList<EdgeWithCost> edgeSmall = vSmall.edges;

		TreeSet<Integer> edgeIndSmall = vSmall.getEdgeIndexes();// 获取临边的定点标号
		edgeSmall.remove(edgeLeastCost);// 从自己的边中删除消耗最小的边
		vBig.edges.remove(edgeLeastCost);

		// 更新Faces: 将vBig中的面片的索引换为vSmall的索引,若vSmall也含有该面片则删除它
		for (FaceTriangle face : vBig.faces) {
			if (face.isVertex(vSmall.index)) {// vSmall也含有该面片,从vSmall的面片中删除,并从面片列表中删除
				vSmall.removeFace(face);
				vertexList.get(face.getAnothorVertex(vSmall.index, vBig.index)).removeFace(face);
				// faces.remove(face);//不需要,只在输出时用到,到时才创建
			} else {
				face.setVertex(vBig.index, vSmall.index);
				vSmall.addFace(face);
			}
		}

		for (EdgeWithCost e : vBig.edges) {
			boolean b = costSet.remove(e);// 先从消耗函数中移除,以改动后删除免出问题
			if ((!edgeIndSmall.contains(e.getOtherVertex(vBig.index)))) {
				e.setVertex(vBig.index, vSmall.index);// 更新边链接坐标,因而不用重新算边
				edgeSmall.add(e);
			} else {
				b = vertexList.get(e.getOtherVertex(vBig.index)).edges.remove(e);// 重复的边,从另一顶点移除
			}
		}

		// 更新vSmall及周围顶点的Q
		vSmall.calculateQ(vertexList);
		for (EdgeWithCost e : edgeSmall) {
			int vInd = e.getOtherVertex(vSmall.index);
			vertexList.get(vInd).calculateQ(vertexList);
		}

		// 更新vSmall的边压缩损耗及周围点的边的的边压缩损耗;
		// 注意:vSmall的边也含在周围的顶点的边中,无需重复更新。
		// 另外,由于不一定这些顶点为较小节点,因而必须全部更新,可能产生重复计算
		for (EdgeWithCost e : edgeSmall) {
			boolean b = costSet.remove(e);// 先删除,否则排序会有错;由于vSmall的周围顶点间可能有边,因而可能重复删除而返回false,不影响
			e.cal_v_cost(vertexList);// 更新边压缩损耗
			costSet.add(e);// 重新加入

		}
		vertexList.set(vBig.index, null);// 标定为null,相当于移除
		vertexNumber--;
	}

	/**
	 * 边收缩操作一次,合并一条消耗最小的边的两个顶点 慢速,更新所有受到影响的边的折叠损耗
	 */
	void contractEdge() {
		// System.err.println(vertexList.size()-vertexNumber+1);
		EdgeWithCost edgeLeastCost = costSet.pollFirst();// 取出消耗最小的边;已从消耗列表中删除
		VertexComplicated vSmall = vertexList.get(edgeLeastCost.getSmallVertex());// 编号小的顶点
		VertexComplicated vBig = vertexList.get(edgeLeastCost.getBigVertex());// 编号大的顶点
		vSmall.setErrorPairContraction(edgeLeastCost.cost);// step2:更新顶点的消耗
		ArrayList<EdgeWithCost> edgeSmall = vSmall.edges;

		TreeSet<Integer> edgeIndSmall = vSmall.getEdgeIndexes();// 获取临边的定点标号

		edgeSmall.remove(edgeLeastCost);// 从自己的边中删除消耗最小的边
		vBig.edges.remove(edgeLeastCost);// 从另一顶点的边中删除消耗最小的边

		// 更新Faces: 将vBig中的面片的索引换为vSmall的索引,若vSmall也含有该面片则删除它
		for (FaceTriangle face : vBig.faces) {
			if (face.isVertex(vSmall.index)) {// vSmall也含有该面片,从vSmall的面片中删除,并从面片列表中删除
				vSmall.removeFace(face);
				vertexList.get(face.getAnothorVertex(vSmall.index, vBig.index)).removeFace(face);
				// faces.remove(face);//不需要,只在输出时用到,到时才创建
			} else {
				face.setVertex(vBig.index, vSmall.index);
				vSmall.addFace(face);
			}
		}

		for (EdgeWithCost e : vBig.edges) {
			boolean b = costSet.remove(e);// 先从消耗函数列表中移除,以免改动后删除出问题
			// if (!b) {
			// // System.out.println(costSet.contains(e));
			// // System.out.flush();
			// // System.err.println(costSet);
			// System.err.println("位置1   移除失败" + e);
			// System.err.flush();
			// }
			if ((!edgeIndSmall.contains(e.getOtherVertex(vBig.index)))) {
				e.setVertex(vBig.index, vSmall.index);// 更新边链接坐标,因而不用重新算边
				edgeSmall.add(e);// 重新加入到消耗函数列表中
			} else {
				b = vertexList.get(e.getOtherVertex(vBig.index)).edges.remove(e);// 重复的边,从另一顶点移除
				// if (!b) {
				// System.err.println("位置3     移除失败" + e);
				// System.err.flush();
				// }
			}
		}

		// 更新vSmall及周围顶点的Q
		vSmall.calculateQ(vertexList);
		for (EdgeWithCost e : edgeSmall) {
			int vInd = e.getOtherVertex(vSmall.index);
			vertexList.get(vInd).calculateQ(vertexList);
		}
		// 更新vSmall的边压缩损耗及周围点的边的的边压缩损耗;
		// 注意:vSmall的边也含在周围的顶点的边中,无需重复更新。
		// 另外,由于不一定这些顶点为较小节点,因而必须全部更新,可能产生重复计算
		for (EdgeWithCost e : edgeSmall) {
			int vInd = e.getOtherVertex(vSmall.index);
			for (EdgeWithCost edge : vertexList.get(vInd).edges) {
				boolean b = costSet.remove(edge);// 先删除,否则排序会有错;由于vSmall的周围顶点间可能有边,因而可能重复删除而返回false,不影响

				// if (!b &&
				// (!vSmall.getEdgeIndexes().contains(edge.getOtherVertex(vSmall.index))))
				// {
				// System.out.flush();
				// System.err.println(costSet);
				// System.err.println("2:移除失败" + edge);
				// System.err.flush();
				// }
				// else{
				// System.err.println("2:                                          移除成功"
				// + edge);
				// }
				edge.cal_v_cost(vertexList);// 更新边压缩损耗
				costSet.add(edge);// 重新加入
			}
		}
		vertexList.set(vBig.index, null);// 标定为null,相当于移除
		vertexNumber--; // 修正当前的点数
	}

	/**
	 * @return 物体的三角面片
	 */
	public HashSet<FaceTriangle> getFaces() {
		HashSet<FaceTriangle> faces = new HashSet<FaceTriangle>(3 * vertexNumber);// 假定面片数量约为顶点的两倍,则最后hashset的loadFactor约为0.7
		for (VertexComplicated v1 : vertexList) {
			if (v1 == null)// 该顶点已被删除
				continue;
			for (FaceTriangle face : v1.faces) {
				faces.add(face);
			}
		}

//		System.out.println("------------面片数量:" + faces.size());
//
//		for (FaceTriangle face : faces) {
//			System.out.println(face);
//		}
		return faces;
	}

	/**
	 * @return 返回顶点索引映射表,第一个为原索引(从0开始),第二个为简化后网格的顶点索引(从1开始)
	 */
	public HashMap<Integer, Integer> getIndexMap() {
		HashMap<Integer, Integer> indexMap = new HashMap<Integer, Integer>(2 * vertexNumber);
		int j = 1;
		for (int i = 0; i < vertexList.size(); i++) {
			if (vertexList.get(i) != null) {
				indexMap.put(i, j);
				j++;
			}
		}

		// for (int i = 0; i < vertexList.size(); i++) {
		// System.out.println(i + "~" + indexMap.get(i));
		// }

		return indexMap;
	}

	/**
	 * 将当前的模型输出到文件
	 * @param fileName 要写入的文件名
	 * @throws IOException
	 */
	public void writeObj(String fileName) throws IOException {
		PrintWriter bw = new PrintWriter(new File(fileName));
		HashMap<Integer, Integer> indexMap = getIndexMap();// 获取索引映射表
		HashSet<FaceTriangle> faces = getFaces();// 获取三角面片的原始索引
		bw.println("# " + vertexNumber + " " + faces.size());// 第一行输出顶点数量和三角面片数量
		for (VertexComplicated v : vertexList) {// 输出顶点
			if (v != null)
				bw.println(v);
		}

		for (FaceTriangle face : faces) {// 输出三角面片,索引要经过修正
			bw.println("f " + indexMap.get(face.v1Ind) + " " + indexMap.get(face.v2Ind) + " "
					+ indexMap.get(face.v3Ind));
		}
		bw.close();
	}

	// EdgeWithCost getEdge(int v1ind, int v2ind) {
	// VertexComplicated v1 = vertexList.get(v1ind);
	// for (EdgeWithCost e : v1.edges) {
	// if (e.getOtherVertex(v1ind) == v2ind)
	// return e;
	// }
	//
	// return null;
	//
	// }

	/**
	 * 简化网格
	 * @param fileName 原始模型的文件名(含路径)
	 * @param fileNameOut 输出模型的文件名(含路径)
	 * @param goalVertex 输出模型的目标顶点数量
	 * @throws IOException
	 */
	public static void simplifyMesh(String fileName, String fileNameOut, int goalVertex)
			throws IOException {
		System
				.setOut(new PrintStream(new File(fileNameOut).getPath() + File.separator
						+ "log.txt"));
		ObjFileSimplified_v2 obj = new ObjFileSimplified_v2(fileName);
		long time = System.currentTimeMillis();
		while (obj.vertexNumber > goalVertex)
			obj.contractEdge();

		System.out.println("边压缩用时:" + (double) (System.currentTimeMillis() - time) / 1000 + "s");
		time = System.currentTimeMillis();

		obj.writeObj(fileNameOut);

		System.out.println("写文件用时:" + (double) (System.currentTimeMillis() - time) / 1000 + "s");

	}

	/**
	 * 简化网格,快速
	 * @param fileName 原始模型的文件名(含路径)
	 * @param fileNameOut 输出模型的文件名(含路径)
	 * @param goalVertex 输出模型的目标顶点数量
	 * @throws IOException
	 */
	public static void simplifyMeshFast(String fileName, String fileNameOut, int goalVertex)
			throws IOException {
		// System
		// .setOut(new PrintStream(new File(fileNameOut).getPath() +
		// File.separator
		// + "log.txt"));
		ObjFileSimplified_v2 obj = new ObjFileSimplified_v2(fileName);
		long time = System.currentTimeMillis();
		while (obj.vertexNumber > goalVertex)
			obj.contractEdgeFast();

		System.out.println("边压缩用时:" + (double) (System.currentTimeMillis() - time) / 1000 + "s");
		time = System.currentTimeMillis();

		obj.writeObj(fileNameOut);

		System.out.println("写文件用时:" + (double) (System.currentTimeMillis() - time) / 1000 + "s");

	}

	public static void main(String[] args) throws IOException {
		// String path = "F:\\课件\\计算机图形学\\模型\\";
		// simplifyMesh(path + "unitcone.obj", path + "LChah.obj", 46);
		test();

		// FaceTriangle fa = new FaceTriangle(1, 2, 4), fc = fa;
		// FaceTriangle fb = new FaceTriangle(1, 2, 4);
		// HashSet<FaceTriangle> faces = new HashSet<FaceTriangle>(100);
		// System.out.println("faces.add(fa)=" + faces.add(fa));
		// System.out.println("faces.add(fa)=" + faces.add(fa));
		//
		// System.out.println(fa);
		//
		// fa.v1Ind = 1060;
		// System.out.println("faces.contains(fa)" + faces.contains(fa));
		// // System.out.println(fa);
		// // System.out.println(fc==fa);
		// System.out.println(fa == fb);
		// for (FaceTriangle face : faces) {
		// System.out.println(face);
		// }
	}

	/**
	 * 编程过程中的一些测试
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private static void test() throws FileNotFoundException, IOException {

		String path = "F:\\课件\\计算机图形学\\模型\\";
		// System.setOut(new PrintStream(new File(path + "log.txt")));

		// ObjFileSimplified obj = new ObjFileSimplified(path + "LC_testQ.obj");
		// ObjFileSimplified obj = new ObjFileSimplified(path +
		// "LCdoubletriangle_testCost.txt");
		// ObjFileSimplified obj = new ObjFileSimplified(path + "unitcone.obj");
		// ObjFileSimplified_v2 obj = new ObjFileSimplified_v2(path +
		// "unitcone.obj");
		ObjFileSimplified_v2 obj = new ObjFileSimplified_v2(path + "cube.obj");
		// obj.getFaces();
		// boolean b = obj.costSet.remove(obj.getEdge(63,86));
		// System.out.println(b);
		// obj.print();
		// System.out.println(obj.costSet);
		obj.getFaces();

		long t = System.currentTimeMillis();
		for (int i = 0; i < obj.vertexList.size() / 2; i++) {
			obj.contractEdge();
			// obj.print();
			// System.out.println(obj.costSet);
		}
		System.out.println("边压缩用时:" + (double) (System.currentTimeMillis() - t) / 1000 + "s");
		t = System.currentTimeMillis();
		obj.writeObj(obj.vertexNumber + ".obj");
		System.out.println("写文件用时:" + (double) (System.currentTimeMillis() - t) / 1000 + "s");

		//		
		//		
		// obj.getIndexMap();
		// float f=0.8888888888888888f;
		// double d=0.8888888888888888;
		// System.out.println(f);
		// System.out.println(d);
		// System.out.println(d-f);
	}

} // End of class ObjectFile

// End of file ObjectFile.java
  • 大小: 30.6 KB
  • 大小: 154.2 KB
  • 大小: 46.3 KB
0
0
分享到:
评论

相关推荐

    基于离散曲率的二次误差度量网格简化算法 (2010年)

    提出了一种基于离散曲率的二次误差度量网格简化算法。在代价函数中引入顶点离散曲率,通过将代价函数作为顶点对的权值来控制顶点对合并次序,更好地保留了原模型的细节特征,同时修改模型特征点与特征线的权值,使得简化...

    论文研究-基于特征保持的半自动化动态网格简化方法.pdf

    基于半边折叠和二次误差度量算法,创新性地提出一种半自动化的网格简化方法。简化系统提供自动误差修正和用户误差修正两种误差修正途径,不仅对已有的简化算法进行了改进,而且还提供了一个友好的用户交互平台,通过...

    网格简化中基于特征矩阵的二次误差测度算法 (2009年)

    针对二次误差测度算法存在尖端特征消失、局部过度简化等缺陷,提出了基于特征矩阵的二次误差测度算法用于网格简化.通过将顶点曲率和边长引进该特征矩阵以优化误差度量,模型中各顶点便易于区分,于是具有明显几何...

    基于四边形折叠的三角网格简化算法 (2008年)

    该算法以四边形折叠为基本操作,利用Garland的二次误差度量(QEM)做误差控制,每次折叠操作可以减少3个顶点及6个面片,从而实现比Garland的QEM算法、周昆等的三角形折叠算法更高的简化效率,文中给出多个试验结果说明了该...

    基于局部多项式拟合的网格简化算法 (2006年)

    为了提高网格简化后的三角形质量,提出了一种新的基于顶点局部多项式曲面拟合的...结果表明,该算法保留了更多的网格细节特征,在简化网格的三角形质量方面优于Garland的二次误差度量算法。该算法可用于快速构建模型的细

    基于加权二次误差测度的边折叠简化算法 (2007年)

    针对许多边折叠网格简化算法在模型进行大规模简化后,不能很好地保持原始模型的重要几何特征,从而产生较严重的视觉失真现象的问题,提出了2种改进的二次误差测度边折叠方法。定义2种三角形重要度并嵌入到原始...

    3D-path-planning-for-mapping-heart_3D路径规划_3dpathplanning_pathpla

    一种三维路径规划算法,将高分辨率网格简化为具有二次误差度量的低分辨率网格

    论文研究-针灸铜人三维可视化研究与应用.pdf

    然后使用XML语言对其承载的传统中医知识进行存储与表达,重点利用基于改进的二次误差度量和递进网格算法实现铜人多分辨率简化模型的自动生成;最后利用Unity引擎开发了一套针灸铜人虚拟交互展示系统,实现了穴位、...

    视觉显着性引导的特征感知形状简化

    在我们的视觉显着图的指导下,可以通过对顶点对收缩的高维特征空间二次误差度量与从我们的视觉显着图得出的权重图进行加权,来执行特征感知形状简化算法。 通过结合网格顶点的位置和法线信息,在六维特征空间中计算...

    gp-cli:用于几何处理的命令行工具

    例如, convertmesh input.ply output.obj或者 convertmesh /path/of/directories/until/folder/containing/mymesh.{off,obj}decimate 使用二次误差度量简化来抽取模型。 例如, decimate 0.1 input.ply output....

    3D游戏卷2:动画与高级实时渲染技术——2

    6.3.3 排序标准——二次误差度量 6.3.4 排序标准——简化外壳 6.4 简化与属性 6.4.1 简化与游戏纹理 6.4.2 简化和蒙皮模型 6.4.3 算法框架 6.4.4 顶点去除算法的重新三角形划分 6.5 实例分析 6.5.1 实例分析1——...

    3D游戏卷2:动画与高级实时渲染技术——1

    6.3.3 排序标准——二次误差度量 6.3.4 排序标准——简化外壳 6.4 简化与属性 6.4.1 简化与游戏纹理 6.4.2 简化和蒙皮模型 6.4.3 算法框架 6.4.4 顶点去除算法的重新三角形划分 6.5 实例分析 6.5.1 实例分析1——...

Global site tag (gtag.js) - Google Analytics