在Unity中使用c#实现简单的软光栅

16 篇文章 2 订阅

在Unity中使用c#实现简单的软光栅

概述

  • 借助Unity的Camera,Matrix4x4,Vector4,Mesh,Texture2D,完成顶点从模型空间到屏幕空间的坐标变换,在屏幕上绘制线框三角形

效果图

  • 右上角即为通过软光栅方式画出来的图
    在这里插入图片描述
  • 通过调整物体的Transform来调整渲染结果
    在这里插入图片描述

实现

using System;
using System.Collections;
using System.Collections.Generic;
using DC;
using UnityEngine;
using UnityEngine.UI;

public class SoftRenderer : MonoBehaviour
{
  public Camera _camera;

  public RawImage _ragImage;

  public Vector2Int _screenSize;

  public Transform _CubeTf;

  public Mesh _Mesh;

  private SoftEngine _softEngine;

  private Device _device;

  void Start()
  {
    _device = new Device();
    _device.Create(_screenSize.x, _screenSize.y);

    _softEngine = new SoftEngine();
    _softEngine.DeviceProp = _device;

    _ragImage.texture = _device.GetResult();
  }

  void Update()
  {
    _device.Clear();

    _softEngine.OnRender(_camera, _Mesh, _CubeTf);
  }

  /*
  local
  world
  view
  projection
  screen
  clip
   */
}

namespace DC
{
  public class SoftEngine
  {
    public Device DeviceProp { get; set; }

    private Color _color = Color.red;

    public Color ColorProp
    {
      get { return _color; }
      set { _color = value; }
    }

    public void OnRender(Camera camera, Mesh mesh, Transform transform)
    {
      var worldMatrix = transform.localToWorldMatrix;
      var viewMatrix = camera.worldToCameraMatrix;
      var projectionMatrix = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false);

      for (var i = 0; i < mesh.triangles.Length - 3; i += 3)
      {
        var pi1 = mesh.triangles[i];
        var pi2 = mesh.triangles[i + 1];
        var pi3 = mesh.triangles[i + 2];

        var lp1 = mesh.vertices[pi1];
        var lp2 = mesh.vertices[pi2];
        var lp3 = mesh.vertices[pi3];

        var mvpMatrix = projectionMatrix * viewMatrix * worldMatrix;

        var lp14 = new Vector4(lp1.x, lp1.y, lp1.z, 1);
        var lp24 = new Vector4(lp2.x, lp2.y, lp2.z, 1);
        var lp34 = new Vector4(lp3.x, lp3.y, lp3.z, 1);

        var mvpp1 = mvpMatrix * lp14;
        var mvpp2 = mvpMatrix * lp24;
        var mvpp3 = mvpMatrix * lp34;

        mvpp1 /= mvpp1.w;
        mvpp2 /= mvpp2.w;
        mvpp3 /= mvpp3.w;

        //todo clip triangle
        var sp1 = ComputeScreenCoordinates(mvpp1);
        var sp2 = ComputeScreenCoordinates(mvpp2);
        var sp3 = ComputeScreenCoordinates(mvpp3);
//                RenderPoint(sp1, ColorProp);
//                RenderPoint(sp2, ColorProp);
//                RenderPoint(sp3, ColorProp);
        RenderTriangle(sp1, sp2, sp3, ColorProp);
      }

//            for (var i = 0; i < mesh.vertexCount; i++)
//            {
//                var mvpMatrix = projectionMatrix * viewMatrix * worldMatrix;
//                var localPoint = mesh.vertices[i];
//                var localPoint4 = new Vector4(localPoint.x, localPoint.y, localPoint.z, 1);
                var worldPoint = worldMatrix.MultiplyPoint(localPoint);
                var viewPoint = viewMatrix.MultiplyPoint(worldPoint);
                var leftViewPoint = rightToLeft.MultiplyPoint(viewPoint);
                var projectionPoint = projectionMatrix.MultiplyPoint(leftViewPoint);
                var mvpPoint = mvpMatrix.MultiplyPoint(localPoint);
//                var mvpPoint4 = mvpMatrix * localPoint4;
//                if (NotClip(mvpPoint4))
//                {
//                    var mvpPoint = mvpPoint4 / mvpPoint4.w;
//                    var screenPos = ComputeScreenCoordinates(mvpPoint);
//                    //                var screenPos2 = ComputeScreenCoordinates(projectionPoint);
//                    //                RenderPoint(screenPos, ColorProp);
//                    RenderPoint(screenPos, ColorProp);
//                }
//            }

      DeviceProp.Commit();
    }

    public static bool NotClip(Vector4 mvpPoint4)
    {
      return Mathf.Abs(mvpPoint4.x) < mvpPoint4.w && Mathf.Abs(mvpPoint4.y) < mvpPoint4.w &&
             Mathf.Abs(mvpPoint4.z) < mvpPoint4.w;
    }

    public Vector2Int ComputeScreenCoordinates(Vector3 projectionPos)
    {
      return new Vector2Int((int) (projectionPos.x * DeviceProp.Width * 0.5f + DeviceProp.Width * 0.5f + 0.5f),
        (int) (projectionPos.y * DeviceProp.Height * 0.5f + DeviceProp.Height * 0.5f + 0.5f));
      //            return new Vector2Int(Convert.ToInt32(projectionPos.x * DeviceProp.Width + 0.5f), Convert.ToInt32(projectionPos.y * DeviceProp.Height));
//            return new Vector2Int((int)(projectionPos.x * DeviceProp.Width + 0.5f + DeviceProp.Width * 0.5f), (int)(projectionPos.y * DeviceProp.Height + 0.5f + DeviceProp.Height * 0.5f));
    }

    public void RenderPoint(Vector2Int point, Color color)
    {
      DeviceProp.SetPixel(point.x, point.y, color);
    }

    public void RenderPoint(int x, int y, Color color)
    {
      DeviceProp.SetPixel(x, y, color);
    }

    public void RenderLine(Vector2Int start, Vector2Int end, Color color)
    {
      var x0 = start.x;
      var y0 = start.y;

      var x1 = end.x;
      var y1 = end.y;
      if (x0 == x1 && y0 == y1)
      {
        RenderPoint(x0,y0, color);
        return;
      }

      var x = x0;
      var y = y0;

      var d = f(x0 + 1, y + 0.5f, x0, y0, x1, y1);

      var delta = Convert.ToInt32(Mathf.Sign(x1 - x0));
      if (x0 == x1)
      {
        delta = Convert.ToInt32(Mathf.Sign(y1 - y0));
        while (y != y1)
        {
          y += delta;
          RenderPoint((int)(x + 0.5f), (int)(y + 0.5f), color);
        }
      }
      else if (y0 == y1)
      {
        while (x != x1)
        {
          x += delta;
          RenderPoint((int)(x + 0.5f), (int)(y + 0.5f), color);
        }
      }
      else
      {
        while (x != x1)
        {
          RenderPoint((int)(x + 0.5f), (int)(y + 0.5f), color);
          //                if (d < 0)
          //                {
          //                    y++;
          //                    d = d + (x1 - x0) + (y0 - y1);
          //                }
          //                else
          //                {
          //                    d = d + (y0 - y1);
          //                }
          x = x + delta;
          y = (int)(calculateY(x, x0, y0, x1, y1) + 0.5f);
        }
      }

      RenderPoint(end.x, end.y, color);

//            var d = 2 * (y0 - y1) * (x0 + 1) + (x1 - x0) * (2 * y0 + 1) + 2 * x0 * y1 - 2 * x1 * y0;
//            for (var x = x0; x <= x1; x++)
//            {
//                RenderPoint(x, y, color);
//                if (d < 0)
//                {
//                    y++;
//                    d = d + 2 * (x1 - x0) + 2 * (y0 - y1);
//                }
//                else
//                {
//                    d = d + 2 * (y0 - y1);
//                }
//            }
    }

    public static float calculateY(float x, float x0, float y0, float x1, float y1)
    {
      return (y1 - y0) * (x - x0) / (x1 - x0) + y0;
    }

    public float f(float x, float y, float x0, float y0, float x1, float y1)
    {
      return (y0 - y1) * x + (x1 - x0) * y + x0 * y1 - x1 * y0;
    }

    public void RenderTriangle(Vector2Int p1, Vector2Int p2, Vector2Int p3, Color color)
    {
      RenderLine(p1, p2, color);
      RenderLine(p2, p3, color);
      RenderLine(p3, p1, color);
    }
  }

  public class Device
  {
    private Texture2D _texture2D;

    public int Width { get; private set; }
    public int Height { get; private set; }

    public void Create(int width, int height)
    {
      this.Width = width;
      this.Height = height;
      _texture2D = new Texture2D(width, height, TextureFormat.ARGB32, false, true);
    }

    public void Clear()
    {
      var black = Color.black;
      for (var x = 0; x < Width; x++)
      {
        for (var y = 0; y < Height; y++)
        {
          SetPixel(x, y, black);
        }
      }
    }

    public void Commit()
    {
      _texture2D.Apply();
    }

    public void SetPixel(int x, int y, Color color)
    {
      _texture2D.SetPixel(x, y, color);
    }

    public Color GetPixel(int x, int y)
    {
      return _texture2D.GetPixel(x, y);
    }

    public Texture2D GetResult()
    {
      return _texture2D;
    }
  }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C# 实现单例模式有多种方式,其一种常见的方式是使用静态变量。下面是一个在 Unity 使用 C# 实现单例的示例代码: ```csharp public class MySingleton : MonoBehaviour { private static MySingleton instance; public static MySingleton Instance { get { if (instance == null) { instance = FindObjectOfType<MySingleton>(); if (instance == null) { GameObject go = new GameObject(); go.name = "MySingleton"; instance = go.AddComponent<MySingleton>(); DontDestroyOnLoad(go); } } return instance; } } private void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } } ``` 这个示例,我们在类定义了一个静态变量 instance,用于存储单例对象。在 Instance 属性,我们首先检查 instance 是否为 null,如果是,就通过 FindObjectOfType 方法查找场景是否已经存在 MySingleton 实例,如果存在就直接赋值 instance,否则就创建一个新的 GameObject,并添加一个 MySingleton 组件,再将其赋值给 instance。最后,我们返回 instance。 在 Awake 方法,我们再次检查 instance 是否为 null,如果是,就将当前实例赋值给 instance,并调用 DontDestroyOnLoad 方法,以便在场景切换时不被销毁。如果 instance 不为 null,说明已经存在 MySingleton 实例了,我们就销毁当前实例。 使用时,我们可以通过 MySingleton.Instance 来获取单例对象。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值