Games103的作业3:跳动的房子(有限元)

mark一下写的,果然,老师说得简单和代码量少对于我等人来说,不是一个维度的。

自己经验少,以为基本单元都是三角形的,这回第一次来了个四面体的,有点蒙圈,但处理过程中也还是把四面体拆成顶点和三角形来处理。确切的说,以往是以为mesh只是个三维空间中闭合的二维面,里面啥都没,就是个空心皮套一样。这回的mesh感觉里面也有三角形。虽说没看过(因为自己不会看模型里面…也有点懒不想找方法比如上色啥的)

这里有个稍微迷惑人的地方,就是初始的名为house的平面,其实它啥用都没…不要以为是神奇的代码把平面折叠成房子,你把FVM脚本附着在啥物体上其实都行。

在课程PPT里,一直是按着二维处理的,这里我们要变成三维,本质上大差不差。

下面记录代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;

public class FVM : MonoBehaviour
{
	float dt 			= 0.003f;
    float mass 			= 1;
	float stiffness_0	= 20000.0f; //lamda
    float stiffness_1 	= 5000.0f;  //miu
    float damp			= 0.999f;

	int[] 		Tet;        
	int tet_number;			//The number of tetrahedra

	Vector3[] 	Force;
	Vector3[] 	V;
	Vector3[] 	X;
	int number;				//The number of vertices

	Matrix4x4[] inv_Dm;

	//For Laplacian smoothing.
	Vector3[]   V_sum;
	int[]		V_num;

	SVD svd = new SVD();

    // Start is called before the first frame update
    void Start()
    {
    	// FILO IO: Read the house model from files.
    	// The model is from Jonathan Schewchuk's Stellar lib.
    	{
    		string fileContent = File.ReadAllText("Assets/house2.ele");
    		string[] Strings = fileContent.Split(new char[]{' ', '\t', '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
    		
    		tet_number=int.Parse(Strings[0]);
        	Tet = new int[tet_number*4];

    		for(int tet=0; tet<tet_number; tet++)
    		{
				Tet[tet*4+0]=int.Parse(Strings[tet*5+4])-1;
				Tet[tet*4+1]=int.Parse(Strings[tet*5+5])-1;
				Tet[tet*4+2]=int.Parse(Strings[tet*5+6])-1;
				Tet[tet*4+3]=int.Parse(Strings[tet*5+7])-1;
			}
    	}
    	{
			string fileContent = File.ReadAllText("Assets/house2.node");
    		string[] Strings = fileContent.Split(new char[]{' ', '\t', '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
    		number = int.Parse(Strings[0]);
    		X = new Vector3[number];
       		for(int i=0; i<number; i++)
       		{
       			X[i].x=float.Parse(Strings[i*5+5])*0.4f;
       			X[i].y=float.Parse(Strings[i*5+6])*0.4f;
       			X[i].z=float.Parse(Strings[i*5+7])*0.4f;
       		}
    		//Centralize the model.
	    	Vector3 center=Vector3.zero;
	    	for(int i=0; i<number; i++)		center+=X[i];
	    	center=center/number;
	    	for(int i=0; i<number; i++)
	    	{
	    		X[i]-=center;
	    		float temp=X[i].y;
	    		X[i].y=X[i].z;
	    		X[i].z=temp;
	    	}
		}
        /*
        tet_number=1;
        Tet = new int[tet_number*4];
        Tet[0]=0;
        Tet[1]=1;
        Tet[2]=2;
        Tet[3]=3;

        number=4;
        X = new Vector3[number];
        V = new Vector3[number];
        Force = new Vector3[number];
        X[0]= new Vector3(0, 0, 0);
        X[1]= new Vector3(1, 0, 0);
        X[2]= new Vector3(0, 1, 0);
        X[3]= new Vector3(0, 0, 1);
        */


        //Create triangle mesh.
       	Vector3[] vertices = new Vector3[tet_number*12];
        int vertex_number=0;
        for(int tet=0; tet<tet_number; tet++)
        {
        	vertices[vertex_number++]=X[Tet[tet*4+0]];
        	vertices[vertex_number++]=X[Tet[tet*4+2]];
        	vertices[vertex_number++]=X[Tet[tet*4+1]];

        	vertices[vertex_number++]=X[Tet[tet*4+0]];
        	vertices[vertex_number++]=X[Tet[tet*4+3]];
        	vertices[vertex_number++]=X[Tet[tet*4+2]];

        	vertices[vertex_number++]=X[Tet[tet*4+0]];
        	vertices[vertex_number++]=X[Tet[tet*4+1]];
        	vertices[vertex_number++]=X[Tet[tet*4+3]];

        	vertices[vertex_number++]=X[Tet[tet*4+1]];
        	vertices[vertex_number++]=X[Tet[tet*4+2]];
        	vertices[vertex_number++]=X[Tet[tet*4+3]];
        }

        int[] triangles = new int[tet_number*12];
        for(int t=0; t<tet_number*4; t++)
        {
        	triangles[t*3+0]=t*3+0;
        	triangles[t*3+1]=t*3+1;
        	triangles[t*3+2]=t*3+2;
        }
        Mesh mesh = GetComponent<MeshFilter> ().mesh;
		mesh.vertices  = vertices;
		mesh.triangles = triangles;
		mesh.RecalculateNormals ();


		V 	  = new Vector3[number];
        Force = new Vector3[number];
        V_sum = new Vector3[number];
        V_num = new int[number];

        //TODO: Need to allocate and assign inv_Dm
        inv_Dm = new Matrix4x4[tet_number]; //初始的位置矩阵
        for (int tet = 0; tet < tet_number; tet++)
            inv_Dm[tet] = Build_Edge_Matrix(tet).inverse;
    }

    Matrix4x4 Build_Edge_Matrix(int tet)
    {
    	Matrix4x4 ret=Matrix4x4.zero;
        //TODO: Need to build edge matrix here.

        ret[0, 0] = X[Tet[tet * 4 + 1]].x - X[Tet[tet * 4 + 0]].x;
        ret[1, 0] = X[Tet[tet * 4 + 1]].y - X[Tet[tet * 4 + 0]].y;
        ret[2, 0] = X[Tet[tet * 4 + 1]].z - X[Tet[tet * 4 + 0]].z;
        ret[3, 0] = 0;

        ret[0, 1] = X[Tet[tet * 4 + 2]].x - X[Tet[tet * 4 + 0]].x;
        ret[1, 1] = X[Tet[tet * 4 + 2]].y - X[Tet[tet * 4 + 0]].y;
        ret[2, 1] = X[Tet[tet * 4 + 2]].z - X[Tet[tet * 4 + 0]].z;
        ret[3, 1] = 0;

        ret[0, 2] = X[Tet[tet * 4 + 3]].x - X[Tet[tet * 4 + 0]].x;
        ret[1, 2] = X[Tet[tet * 4 + 3]].y - X[Tet[tet * 4 + 0]].y;
        ret[2, 2] = X[Tet[tet * 4 + 3]].z - X[Tet[tet * 4 + 0]].z;
        ret[3, 2] = 0;

        ret[0, 3] = 0;
        ret[1, 3] = 0;
        ret[2, 3] = 0;
        ret[3, 3] = 1;
        return ret;
    }

    void Smooth_V()
    {
        for (int i = 0; i < number; i++)
        {
            V_sum[i] = new Vector3(0, 0, 0);
            V_num[i] = 0;
        }

        for (int tet = 0; tet < tet_number; tet++)
        {
            Vector3 sum = V[Tet[tet * 4 + 0]] + V[Tet[tet * 4 + 1]] + V[Tet[tet * 4 + 2]] + V[Tet[tet * 4 + 3]];
            V_sum[Tet[tet * 4 + 0]] += sum;
            V_sum[Tet[tet * 4 + 1]] += sum;
            V_sum[Tet[tet * 4 + 2]] += sum;
            V_sum[Tet[tet * 4 + 3]] += sum;
            V_num[Tet[tet * 4 + 0]] += 4;
            V_num[Tet[tet * 4 + 1]] += 4;
            V_num[Tet[tet * 4 + 2]] += 4;
            V_num[Tet[tet * 4 + 3]] += 4;
        }

        for (int i = 0; i < number; i++)
        {
            V[i] = 0.9f * V[i] + 0.1f * V_sum[i] / V_num[i];
        }
    }


    void _Update()
    {
    	// Jump up.
		if(Input.GetKeyDown(KeyCode.Space))
    	{
    		for(int i=0; i<number; i++)
    			V[i].y+=0.2f;
    	}

    	for(int i=0 ;i<number; i++)
    	{
            //TODO: Add gravity to Force.
            Force[i] = new Vector3(0,- 9.8f * mass, 0);
    	}

    	for(int tet=0; tet<tet_number; tet++)
    	{
            //TODO: Deformation Gradient
            Matrix4x4 F = Build_Edge_Matrix(tet) * inv_Dm[tet];
            //TODO: Green Strain
            Matrix4x4 G = F.transpose * F;
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 4; j++)
                {
                    if (i == j)
                        G[i, j] = G[i, j] - 1;
                    G[i, j] = 0.5f * G[i, j];
                }
            //TODO: Second PK Stress
            Matrix4x4 S = Matrix4x4.zero;
            float traceG = G[0, 0] + G[1, 1] + G[2, 2];
            for(int i=0;i<4;i++)
                for(int j=0;j<4;j++)
                {
                    S[i, j] = 2 * stiffness_1 * G[i, j];
                    if (i == j)
                        S[i, j] += stiffness_0 * traceG;
                }
            //TODO: Elastic Force
            Matrix4x4 force = F * S * inv_Dm[tet].transpose;
            float volume = 1/(inv_Dm[tet].determinant * 6); //注意下这里体积公式 S=||A||/6  S=1/(6*||A^-1||)
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 4; j++)
                    force[i, j] = -1*volume * force[i, j];
            Force[Tet[tet * 4 + 1]].x += force[0, 0];
            Force[Tet[tet * 4 + 1]].y += force[1, 0];
            Force[Tet[tet * 4 + 1]].z += force[2, 0];
            Force[Tet[tet * 4 + 2]].x += force[0, 1];
            Force[Tet[tet * 4 + 2]].y += force[1, 1];
            Force[Tet[tet * 4 + 2]].z += force[2, 1];
            Force[Tet[tet * 4 + 3]].x += force[0, 2];
            Force[Tet[tet * 4 + 3]].y += force[1, 2];
            Force[Tet[tet * 4 + 3]].z += force[2, 2];

            Force[Tet[tet * 4 + 0]].x -= (force[0, 0] + force[0, 1] + force[0, 2]);
            Force[Tet[tet * 4 + 0]].y -= (force[1, 0] + force[1, 1] + force[1, 2]);
            Force[Tet[tet * 4 + 0]].z -= (force[2, 0] + force[2, 1] + force[2, 2]);
        }

        Smooth_V();



        for (int i=0; i<number; i++)
    	{
            //TODO: Update X and V here.
            V[i] = (V[i] + dt * Force[i] / mass) * damp;
            X[i] = X[i] + V[i] * dt;
    		//TODO: (Particle) collision with floor.
            if(X[i].y<-3f)   //这里仅对局部坐标进行相应判断,选取-3的原因是底部物体的世界坐标是-3
            {
                X[i].y = -3f;
                if (V[i].y < 0)
                    V[i].y = -V[i].y;
            }
    	}
    }

    // Update is called once per frame
    void Update()
    {
    	for(int l=0; l<10; l++)
    		 _Update();

    	// Dump the vertex array for rendering.
    	Vector3[] vertices = new Vector3[tet_number*12];
        int vertex_number=0;
        for(int tet=0; tet<tet_number; tet++)
        {
        	vertices[vertex_number++]=X[Tet[tet*4+0]];
        	vertices[vertex_number++]=X[Tet[tet*4+2]];
        	vertices[vertex_number++]=X[Tet[tet*4+1]];
        	vertices[vertex_number++]=X[Tet[tet*4+0]];
        	vertices[vertex_number++]=X[Tet[tet*4+3]];
        	vertices[vertex_number++]=X[Tet[tet*4+2]];
        	vertices[vertex_number++]=X[Tet[tet*4+0]];
        	vertices[vertex_number++]=X[Tet[tet*4+1]];
        	vertices[vertex_number++]=X[Tet[tet*4+3]];
        	vertices[vertex_number++]=X[Tet[tet*4+1]];
        	vertices[vertex_number++]=X[Tet[tet*4+2]];
        	vertices[vertex_number++]=X[Tet[tet*4+3]];
        }
        Mesh mesh = GetComponent<MeshFilter> ().mesh;
		mesh.vertices  = vertices;
		mesh.RecalculateNormals ();
    }
}

pipeline and shading是计算机图形学中的两个重要概念。在Games101课程中,我们学习了这两个概念以及它们在游戏开发中的应用。 首先,管线(pipeline)是指图形渲染过程中的一系列阶段。这些阶段依次执行,以将3D模型转换为最终的2D图像。管线的主要阶段包括顶点处理、光栅化和片元处理。在顶点处理阶段,模型的顶点坐标被转换为屏幕空间坐标,并进行光照计算等操作。在光栅化阶段,模型的三角形被转化为像素,在屏幕上显示。在片元处理阶段,对每个像素执行颜色计算,以确定最终的像素颜色。 其次,着色(shading)是指确定模型表面每个像素的颜色和光照效果。常见的着色模型包括平面着色、高洛德着色和菲格斯特随机着色等。平面着色是最简单的一种,忽略了光照效果,给每个像素分配一个固定的颜色。高洛德着色则考虑了光照的方向和强度,使得模型表面看起来更加真实。菲格斯特随机着色则引入了随机过程,给模型表面增加纹理和细节。 在游戏开发中,管线和着色技术被广泛应用。通过管线的处理,可以实现更高的渲染效率和图像质量。而着色技术可以使游戏中的模型更加真实,增加玩家的沉浸感。此外,我们还可以通过改变管线和着色的参数来实现特殊的效果,如阴影、光线投射和折射等。 总的来说,pipeline and shading是计算机图形学中的重要概念。在Games101课程中,我们深入学习了这两个概念以及它们在游戏开发中的应用。通过理解和掌握管线和着色技术,我们可以实现更加高效和逼真的游戏图形效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值