1、Unity体绘制显示

1、前言

和在opengl中实现一样,unity通过texture3D读取体数据,通过着色器处理,从而使用GPU进行计算渲染。

2、Texture3D的读取方式

代码生成Texture3D:

using UnityEngine;

public class Create3DTex : MonoBehaviour
{
    [SerializeField]
    int size = 16;

    void Start()
    {
        var tex = new Texture3D(size, size, size, TextureFormat.ARGB32, true);
        var colors = new Color[size * size * size];

        float a = 1f / (size - 1);
        int i = 0;
        Color c = Color.white;

        for (int z = 0; z < size; ++z)
        {
            for (int y = 0; y < size; ++y)
            {
                for (int x = 0; x < size; ++x, ++i)
                {
                    c.r = ((x & 1) != 0) ? x * a : 1 - x * a;
                    c.g = ((y & 1) != 0) ? y * a : 1 - y * a;
                    c.b = ((z & 1) != 0) ? z * a : 1 - z * a;
                    colors[i] = c;
                }
            }
        }

        tex.SetPixels(colors);
        tex.Apply();

        var renderer = GetComponent<Renderer>();
        renderer.material.SetTexture("_Volume", tex);
    }
}

读取Texture3D

Shader "VolumeRendering/3D Texture"
{

Properties
{
    _Volume("Volume", 3D) = "" {}
}

CGINCLUDE

#include "UnityCG.cginc"

struct appdata
{
    float4 vertex : POSITION;
};

struct v2f
{
    float4 vertex : SV_POSITION;
    float3 uv : TEXCOORD0;
};

sampler3D _Volume;

v2f vert(appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.vertex.xyz * 0.5 + 0.5;
    return o;
}

fixed4 frag(v2f i) : SV_Target
{
    return tex3D(_Volume, i.uv);
}

ENDCG

SubShader
{

Tags 
{ 
    "RenderType"="Opaque" 
}

Pass
{
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    ENDCG
}

}

}

多边形表面的3d纹理的颜色被描绘。像下面这样改变顶点着色器的代码,通过改变或移动图形的大小,就可以用世界坐标消减想看的3d纹理的部位。

v2f vert(appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    float4 wpos = mul(unity_ObjectToWorld, v.vertex);
    o.uv = wpos.xyz * 0.5 + 0.5;
    return o;
}

3、直接读取3D体数据

将原始数据直接作为3d纹理使用。

using UnityEngine;
using UnityEditor;
using UnityEditor.Experimental.AssetImporters;
using System;
using System.IO;

[ScriptedImporter(1, "raw")]
public class PvmRawImporter : ScriptedImporter
{
    public enum Bits
    {
        Eight = 1,
        Sixteen = 2,
    }

    public int width = 256;
    public int height = 256;
    public int depth = 256;
    public Bits bit = Bits.Eight;

    int totalSize
    {
        get { return width * height * depth * (int)bit; }
    }

    int maxValueSize
    {
        get 
        {
            switch (bit)
            {
                case Bits.Eight   : return (int)Byte.MaxValue;
                case Bits.Sixteen : return (int)UInt16.MaxValue;
                default:
                    throw new Exception("bit is wrong.");
            }
        }
    }

    Texture3D GetTexture3D(string path)
    {
        using (var stream = new FileStream(path, FileMode.Open))
        {
            if (stream.Length != totalSize) 
            { 
                throw new Exception("Data size is wrong."); 
            }

            int n = totalSize;
            var colors = new Color[n];
            float a = 1f / maxValueSize;
            var buf = new byte[(int)bit];

            for (int i = 0; i < n; ++i)
            {
                float value = 0f;
                switch (bit)
                {
                    case Bits.Eight:
                        var b = stream.ReadByte();
                        value = a * b;
                        break;
                    case Bits.Sixteen:
                        stream.Read(buf, 0, 2);
                        value = a * BitConverter.ToUInt16(buf, 0);
                        break;
                }
                colors[i] = new Color(value, value, value, value);
            }

            var tex3d = new Texture3D(width, height, depth, TextureFormat.RGBA32, false);
            tex3d.SetPixels(colors, 0);

            return tex3d;
        }
    }

    public override void OnImportAsset(AssetImportContext ctx)
    {
        try
        {
            var tex3d = GetTexture3D(ctx.assetPath);
            ctx.AddObjectToAsset("Volume", tex3d);
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
    }
}

4、raycasting进行体渲染

Shader "VolumeRendering/VolumeRendering"
{

Properties
{
    _Volume("Volume", 3D) = "" {}
    _Color("Color", Color) = (1, 1, 1, 1)
    _Iteration("Iteration", Int) = 10
    _Intensity("Intensity", Range(0, 1)) = 0.1
}

CGINCLUDE

#include "UnityCG.cginc"

struct appdata
{
    float4 vertex : POSITION;
};

struct v2f
{
    float4 vertex   : SV_POSITION;
    float4 localPos : TEXCOORD0;
    float4 worldPos : TEXCOORD1;
};

sampler3D _Volume;
fixed4 _Color;
int _Iteration;
fixed _Intensity;

v2f vert(appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.localPos = v.vertex;
    o.worldPos = mul(unity_ObjectToWorld, v.vertex);
    return o;
}

fixed4 frag(v2f i) : SV_Target
{
    float3 wdir = i.worldPos - _WorldSpaceCameraPos;
    float3 ldir = normalize(mul(unity_WorldToObject, wdir));
    float3 lstep = ldir / _Iteration;
    float3 lpos = i.localPos;
    fixed output = 0.0;

    [loop]
    for (int i = 0; i < _Iteration; ++i)
    {
        fixed a = tex3D(_Volume, lpos + 0.5).r;
        output += (1 - output) * a * _Intensity;
        lpos += lstep;
        if (!all(max(0.5 - abs(lpos), 0.0))) break;
    }

    return _Color * output;
}

ENDCG

SubShader
{

Tags 
{ 
    "Queue" = "Transparent"
    "RenderType" = "Transparent" 
}

Pass
{
    Cull Back
    ZWrite Off
    Blend SrcAlpha OneMinusSrcAlpha 
    Lighting Off

    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    ENDCG
}

}

}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值