Unity3D學習筆記2——繪制一個帶紋理的面


1. 概述

上一篇文章《Unity3D學習筆記1——繪制一個三角形》中介紹了Unity3D的HelloWorld——繪制一個簡單的三角形。不過這個三角形太簡單了,連材質都沒有。那么這里就將三角形擴展為一個矩形的面,並且為這個面貼上紋理。

2. 詳論

2.1. 網格(Mesh)

前面說到網格是渲染物體的骨架,因此還是先要把渲染物體的架子搭好。改進一下上一篇文章中的創建Mesh的代碼:

Mesh mesh = new Mesh();
mesh.name = name;

Vector3[] vertices = new Vector3[4]
{
    new Vector3(-5, -5, 0),
    new Vector3(-5, 5, 0),
    new Vector3(5, -5, 0),
    new Vector3(5, 5, 0),
};
mesh.vertices = vertices;

Vector2[] uv = new Vector2[4]
{
    new Vector2(0, 0),
    new Vector2(0, 1),
    new Vector2(1, 0),
    new Vector2(1, 1),
};
mesh.uv = uv;

Vector3[] normals = new Vector3[4]
{
    new Vector3(0, 0, -1),
    new Vector3(0, 0, -1),
    new Vector3(0, 0, -1),
    new Vector3(0, 0, -1),
};
mesh.normals = normals;
//mesh.RecalculateNormals();

int[] triangles = new int[6] { 0, 1, 2, 1, 3, 2 };
mesh.triangles = triangles;

GameObject newGameObject = new GameObject(name);
MeshFilter mf = newGameObject.AddComponent<MeshFilter>();
mf.sharedMesh = mesh;

2.1.1. 頂點

因為我們要創建一個矩形的面,所以需要創建四個頂點。仍然是像之前創建三角面的頂點一樣,賦予頂點的空間位置屬性xyz坐標。同時,我們還給Mesh賦予了4個uv坐標,4個法向量normal。uv坐標是用來計算紋理坐標的,也就是當物體貼上紋理之后的紋理坐標位置;法向量是用來參與光照計算的,如果缺少法向量,很多材質的效果不正確。可以通過mesh.RecalculateNormals()讓Unity3D自己計算法向量。

位置(position/vertice)、紋理坐標(uv/texCoord)、法向量(normal)是經常用到了三個頂點屬性,但是頂點屬性也不僅僅只有三個,甚至可以根據需要自定義。

2.1.2. 頂點索引

一個矩形面確定了四個頂點,但是需要划分成兩個三角形,每個三角形引用3個頂點索引,也就是6個頂點索引。當然我們也可以使用6個頂點,按照自然順序來確定頂點索引。但是這樣一來,就浪費了空間存儲。這也是使用頂點索引的好處,可以節省空間,畢竟Mesh中的很多頂點是共用的。

2.2. 材質(Material)

接下來我們在Unity3D編輯器中創建一個材質,並且在C#腳本中將這個材質給到我們創建的面上。

2.2.1. 創建材質

材質和紋理(圖片)在Unity3D中被認為是一種資源,要加載他們需要特定的辦法。一種比較簡單的辦法是使用Resources.Load。

在Assets目錄下創建一個名為Resources的文件夾,只有使用這個目錄下的資源,使用Resources.Load才能找到。在Resources文件夾下新建一個材質,並把想使用的紋理圖片文件移到這個文件夾下:
imglink1

點擊新建的材質,在Inspector視圖中,將紋理圖片掛載到這個材質上:
imglink2

Unity3D新建的材質默認為標准,是一種PBR材質,由多種貼圖混合而成。我們這里暫時只設置Albedo貼圖,也就是基本顏色貼圖。實際使用時,右邊的顏色拾取也能影響到貼圖效果,在有貼圖時,可以將其拾取成白色。

2.2.2. 使用材質

在編輯器中把材質創建好之后,在腳本中就可以直接使用創建好的材質了:

MeshRenderer meshRenderer = newGameObject.AddComponent<MeshRenderer>();                
Material material = Resources.Load<Material>("MaterialDemo");   
meshRenderer.material = material;

2.3. 光照

點擊Play,會發現雖然顯示了一個帶紋理的面,但是面的顏色顯得很暗:
imglink3

這是因為光照的位置不對,材質缺少對光照的影響。那么我們調整默認光照Directional Light的Transform,將其調整到和攝像機的位置一致:
imglink4

這個時候的光照正好對准了面的正中間:
imglink5

最終Game視圖中的面也按照正常亮度顯示了:
imglink6

3. 代碼

全部的C#腳本代碼如下:

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

public class Main : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        GameObject main = GameObject.Find("/Root");
        if (main == null)
        {
            return;
        }

        GameObject triangleGameObject = GreateQuad();
        triangleGameObject.transform.parent = main.transform;
    }

    GameObject GreateQuad()
    {
        string name = "quad";

        Mesh mesh = new Mesh();
        mesh.name = name;

        Vector3[] vertices = new Vector3[4]
        {
            new Vector3(-5, -5, 0),
            new Vector3(-5, 5, 0),
            new Vector3(5, -5, 0),
            new Vector3(5, 5, 0),
        };
        mesh.vertices = vertices;

        Vector2[] uv = new Vector2[4]
        {
            new Vector2(0, 0),
            new Vector2(0, 1),
            new Vector2(1, 0),
            new Vector2(1, 1),
        };
        mesh.uv = uv;

        Vector3[] normals = new Vector3[4]
        {
            new Vector3(0, 0, -1),
            new Vector3(0, 0, -1),
            new Vector3(0, 0, -1),
            new Vector3(0, 0, -1),
        };
        mesh.normals = normals;
        //mesh.RecalculateNormals();

        int[] triangles = new int[6] { 0, 1, 2, 1, 3, 2 };
        mesh.triangles = triangles;
               
        GameObject newGameObject = new GameObject(name);
        MeshFilter mf = newGameObject.AddComponent<MeshFilter>();
        mf.sharedMesh = mesh;

        MeshRenderer meshRenderer = newGameObject.AddComponent<MeshRenderer>();                
        Material material = Resources.Load<Material>("MaterialDemo");   
        meshRenderer.material = material;

        return newGameObject;
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM