3d 贴图一般在 Houdini等软件生成,毕竟需要其他软件支持,导入导出预览效率和制作效率都不高 (Houdini也不便宜....别问就是穷...)

Unity中只见到过一些3D噪音图的生成,本文提供一个任意物体(Mesh)的生成3dTexture方法,水平有限如果高见不胜赐教

知乎视频​www.zhihu.com

基本流程

1.物体shader 使用VFACE 判断模正反 给予不同的颜色

2.shader 中调整裁剪clip的值,对模型进行切片,并使用相机将每片拍下来保存为Texture2D 数组

3.将保存的Texture2D数组 生成为Texture3D

4.将生成完的Texture3D 重新载入另一个shader进行模糊,再次切片 合成导出,(3D贴图的模糊需要采集 前后左右上下,因为是一个3维的,如果你想深入了解 可以参考这篇 Nvdia 的Fast Third-Order Texture Filtering )

效果图

128x128x128
256x256x256
哈哈 64x64x64 点采样,有点mc了

创建的速度比较理想,128 分辨率以内的都可以在1秒内创建完成

非模糊时创建的速度
模糊时创建的速度

首先是切片的shader ,使用VFACE 判断正反,内白外黑

Shader "sliceSahder"
{Properties{_facadeColor("FacadeColor", Color) = (1,1,1,1)_backColor("BackColor", Color) = (1,1,1,1)_height("Height", Range(-1,1)) = 0.0}SubShader{Tags{"DisableBatching" = "True""Queue" = "Transparent""RenderType" = "Transparent"}Pass{Tags {"RenderType" = "Opaque" "Queue" = "Geometry"}Zwrite OnCull Off AlphaToMask On //改善AlphaTest抗锯齿CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;};struct v2f{float4 vertex : SV_POSITION;float3 worldPos : TEXCOORD0;};float _height;float4 _backColor, _facadeColor;float4 _SliceObjectPos;v2f vert(appdata v){v2f o = (v2f)0;o.vertex = UnityObjectToClipPos(v.vertex);float3 wPos = mul(unity_ObjectToWorld, v.vertex.xyz);//_SliceObjectPos是模型的世界空间位置,我们要控制切片范围在box内o.worldPos = wPos + _SliceObjectPos .xyz;return o;}float4 frag(v2f i, fixed facing : VFACE) : SV_Target{float4 heightResult = step(i.worldPos.y + _height,0);float4 facade = heightResult * _facadeColor;float4 back = heightResult * _backColor;float4 finalColor = 0;if (facing > 0){finalColor = facade;}else{finalColor = back;}clip(finalColor.a - 0.5);return finalColor;}ENDCG}}
}

我们控制 _height的值就可以控制 切片的高度

忘记说一下,我们首先需要创建一个切片区域,先画一个1立方米的盒子

controlBox.cs

    private void OnDrawGizmos(){Gizmos.color = boxColor * new Color(0.5f, 0.5f, 0.5f, 0.5f);Gizmos.DrawWireCube(transform.position, transform.localScale);}

下面是 controlBoxEditor.cs 中的内容

创建一个摄像机与这个Box 匹配,注意要让摄像机背景为黑色

public Camera CreateCamera(){controlBox controlFun = (controlBox)target;if (controlGameObject != null){return xRayCamera;}else{controlGameObject = new GameObject("SliceCam");controlGameObject.transform.SetParent(controlFun.transform);controlGameObject.hideFlags = HideFlags.DontSave;Vector3 LocalScale = controlFun.transform.localScale;float CamPos = LocalScale.y / 2;//保正相机在box 顶部向下看controlGameObject.transform.position = controlFun.transform.position + new Vector3(0, CamPos, 0);controlGameObject.transform.localEulerAngles = new Vector3(90, 0, 0);xRayCamera = controlGameObject.AddComponent<Camera>();xRayCamera.clearFlags = CameraClearFlags.SolidColor;xRayCamera.backgroundColor = Color.black;//背景要设置黑色xRayCamera.orthographic = true;xRayCamera.orthographicSize = CamPos;xRayCamera.nearClipPlane = 0;//box最顶面xRayCamera.farClipPlane = LocalScale.y;//box最底面xRayCamera.enabled = true;xRayCamera.renderingPath = RenderingPath.Forward;int maskLayer = 1 << 31;//设置一个专用层xRayCamera.cullingMask = maskLayer;return xRayCamera;}}

这时相机中的效果就是这样

下一步就是在循环中一张张的渲染所有切片保存到Texture2D数组备用

float RamapValue(float original, float oldMinVal, float oldMaxVal, float newMinVal, float newMaxVal){float calculate = (original - oldMinVal) * (newMaxVal - newMinVal) / (oldMaxVal - oldMinVal) + newMinVal;return calculate;}private void CreateTextureArray(bool Blur){controlBox controlFun = (controlBox)target;int sliceCount = controlFun.SliceCountLayer;Texture3DArray = new Texture2D[sliceCount];for (int i = 0; i < sliceCount; i++){serializedObject.Update();//如果是Editor的类一定要加这个,否则不刷新//使用 Alpha8 减少贴图大小RenderTexture xRayLayer = new RenderTexture(sliceCount, sliceCount, 24, RenderTextureFormat.R8);// 重映射值,如设置128层,shader中(-0.5,0.5) 那么每层步进 0.007813 float setLayer = RamapValue(i, 0, sliceCount(128), controlFun.MinMax.x(-0.5), controlFun.MinMax.y(0.5));if (Blur){controlFun.BlurMaterial.SetTexture("_VolumeTex", BuildTexture3D);controlFun.BlurMaterial.SetFloat("_offset", setLayer);}else{controlFun.SliceMaterial.SetFloat("_height", setLayer);//设置每层偏移参数}xRayCamera.targetTexture = xRayLayer;xRayCamera.Render();Texture2D texture2D = new Texture2D(sliceCount, sliceCount, TextureFormat.R8, false);RenderTexture.active = xRayLayer;texture2D.ReadPixels(new Rect(0, 0, sliceCount, sliceCount), 0, 0);texture2D.Apply();Texture3DArray[i] = texture2D;//这时也可以保存每层出来看一下//bytes = texture2D.EncodeToPNG();//File.WriteAllBytes("Assets/Texture3DTemp/My3DTexture" + i + ".png", bytes);serializedObject.ApplyModifiedProperties();}//AssetDatabase.ImportAsset("Assets/Texture3DTemp");}

然后就可以创建3D 贴图了 (如果模糊的话这里要走两遍)

public void Create3DTexture(bool blur, bool save){CreateCamera();CreateTextureArray(blur);int width = Texture3DArray[0].width;int height = Texture3DArray[0].height;int depth = Texture3DArray.Length;BuildTexture3D = new Texture3D(width, height, depth, TextureFormat.R8, true);var color3d = new Color[width * height * depth];int idx = 0;for (int z = depth-1; z >= 0; z--) //反向读取 写入3D贴图{UIProgressBar(true, blur, (int)RamapValue(z, depth,0,0, depth));//进度条显示Texture2D loadedtexture = Texture3DArray[z];for (int y = 0; y < height; ++y){for (int x = 0; x < width; ++x, ++idx){color3d[idx] = loadedtexture.GetPixel(x, y);}}}UIProgressBar(false, blur);BuildTexture3D.SetPixels(color3d);BuildTexture3D.Apply();if (save)// 如果进行模糊,第一遍不用保存到硬盘{AssetDatabase.CreateAsset(BuildTexture3D, "Assets/My3DTexture.asset");DestroyImmediate(controlGameObject);Texture3DArray = null;BuildTexture3D = null;xRayCamera = null;controlGameObject = null;}}

下面是3D 贴图模糊时使用的shader

Shader "Unlit/sliceBlurShader"
{Properties{_clearColor("ClearColor",Color) = (1,1,1,1)_VolumeTex("VolumeTex",3D) = "black"{}_offset("Offset", float) = 0_stepSize("StepSize",range(0,2)) = 1_transparency("Transparency",float) = 1_opacityCorrection ("OpacityCorrection",float) = 1}SubShader{Tags { "RenderType"="Transparent" }Pass{ZWrite OffBlend One OneMinusSrcAlphaCull OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 posW :  TEXCOORD1;float4 vertex : SV_POSITION;};Texture3D<float4> _VolumeTex;SamplerState  sampler_VolumeTex;float _offset;float _stepSize;float _opacityCorrection;float4 _clearColor;float _transparency;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(float4(v.vertex.xyz, 1.0));o.uv = v.uv;o.posW = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1.0));return o;}fixed4 frag(v2f i) : SV_Target{       float3 tex3DUvw = float3(i.posW.x,i.posW.y + _offset,i.posW.z );float colorSample = 0;float colAcc = 0;float dx = 0.5;//偏移UVfloat3 vTAR = float3(dx, dx, dx) * _stepSize;float3 vTAL = float3(dx, dx, -dx) * _stepSize;float3 vTPR = float3(dx, -dx, dx) * _stepSize;float3 vTPL = float3(dx, -dx, -dx) * _stepSize;float3 vBAR = float3(-dx, dx, dx) * _stepSize;float3 vBAL = float3(-dx, dx, -dx) * _stepSize;float3 vBPR = float3(-dx, -dx, dx) * _stepSize;float3 vBPL = float3(-dx, -dx, -dx) * _stepSize;//利用mipmap 代替downSamplefor (int mipLevel = 0; mipLevel <5; mipLevel++){colorSample += _VolumeTex.SampleLevel(sampler_VolumeTex, tex3DUvw + vTAR, mipLevel);colorSample += _VolumeTex.SampleLevel(sampler_VolumeTex, tex3DUvw + vTAL, mipLevel);colorSample += _VolumeTex.SampleLevel(sampler_VolumeTex, tex3DUvw + vTPR, mipLevel);colorSample += _VolumeTex.SampleLevel(sampler_VolumeTex, tex3DUvw + vTPL, mipLevel);colorSample += _VolumeTex.SampleLevel(sampler_VolumeTex, tex3DUvw + vBAR, mipLevel);colorSample += _VolumeTex.SampleLevel(sampler_VolumeTex, tex3DUvw + vBAL, mipLevel);colorSample += _VolumeTex.SampleLevel(sampler_VolumeTex, tex3DUvw + vBPR, mipLevel);colorSample += _VolumeTex.SampleLevel(sampler_VolumeTex, tex3DUvw + vBPL, mipLevel);colorSample *= 0.125;colAcc = (1.0 - colAcc.r) * colorSample + colAcc;}return saturate(pow(colAcc.r * _transparency, _opacityCorrection));}ENDCG}}
}

无模糊和有模糊对比,模糊后边缘的衰减可以添加细节噪音 更柔和

顺便说下,不使用RayMarch 也是可以简单的预览3D贴图的 上面的效果就是使用下列代码创建的

using UnityEngine;public class LoadTexture3D : MonoBehaviour
{public Material sliceMaterial;//使用模糊shader 创建一个材质球指引到这public int textureLength = 64;private MeshRenderer[] meshRenders;private GameObject gameObject;private GameObject planeObject;void Start(){meshRenders = new MeshRenderer[textureLength - 1];gameObject = GameObject.CreatePrimitive(PrimitiveType.Quad);gameObject.GetComponent<MeshRenderer>().sharedMaterial = sliceMaterial;gameObject.transform.position = transform.position;gameObject.transform.SetParent(transform);gameObject.transform.rotation = Quaternion.Euler(90, 0, 0);for (int i = 0; i < textureLength - 1; i++){Vector3 pos = new Vector3(transform.position.x, transform.position.y + i * (1f / textureLength) - 0.5f, transform.position.z );planeObject = Instantiate(gameObject);planeObject.transform.position = pos;planeObject.transform.SetParent(transform);meshRenders[i] = planeObject.GetComponent<MeshRenderer>();}sliceMaterial.SetFloat("_offset", 0);}}

体积云渲染相关可以看这篇

面向搜索引擎编程:RayMarching实时体积云渲染入门(上)​zhuanlan.zhihu.com

参考 https://gamedev.stackexchange.com/questions/26649/glsl-one-pass-gaussian-blur

unity.生成表示地图信息的二维数组_Unity3D 中生成任意形状3D Texture amp; 体积云...相关推荐

  1. php 二维数组根据键值合并二维数组_php数组根据某键值,把相同键值的合并最终生成一个新的二维数组...

    匿名用户 1级 2013-12-29 回答 php数组根据某一个键值,把相同键值的合并生成一个新的二维数组 源数据: $infos = array( array( 'a' => 36, 'b' ...

  2. python创建一个n*m的二维数组_Python中创建二维数组

    Python中创建二维数组 二维数组 二维数组本质上是以数组作为数组元素的数组,即"数组的数组",类型说明符 数组名[常量表达式][常量表达式].二维数组又称为矩阵,行列数相等的矩 ...

  3. Java黑皮书课后题第8章:*8.31(几何:交点)编写一个方法,返回两条直线的交点。四个点存放在4*2的二维数组points中。编写一个程序,提示用户输入4个点,并显示交点

    *8.31(几何:交点)编写一个方法,返回两条直线的交点.四个点存放在4*2的二维数组points中.编写一个程序,提示用户输入4个点,并显示交点 题目 题目描述 破题 代码 本题运行实例 题目 题目 ...

  4. python构建二维数组_Python中创建二维数组

    二维数组 二维数组本质上是以数组作为数组元素的数组,即"数组的数组",类型说明符 数组名[常量表达式][常量表达式].二维数组又称为矩阵,行列数相等的矩阵称为方阵.对称矩阵a[i] ...

  5. 将一个数组a的行和列的元素互换,存到另一个二维数组b中

    将一个数组a的行和列的元素互换,存到另一个二维数组b中 #include<stdio.h> int main() { int a[2][3]={{1,2,3},{4,5,6}}; int ...

  6. c语言二维数组a中,a,a[0],a[0][0]的值与值的类型

    c语言二维数组中的一些表达式的值与意义的问题 前两天写代码的时候遇到一些关于数组的问题,进而对二维数组进行了一些深入的思考.想到了一个有意思的问题. 在二维数组a中,&a,&a[0], ...

  7. php循环构建二维数组,php循环生成二维数组

    程序中定义了二维数组arr,arr有3行4列共12个元素,程序中采用for循环语句的嵌套来计算数组中各个元素的和,并将结果保存在sum变量中,最后打印输出结果. public ...... 1. 从数 ...

  8. python list二维数组_python中的二维数组和lamda

    原博文 2016-11-16 15:15 − python列表推导式 list=[[0 for i in xrange(3)] for j in xrange(4)] 二维数组 g=lambda x, ...

  9. js二维数组arr中表示读取第i行第j列的是:_c++ c语言 数组与字符串

    c语法7 - 数组与字符串 概述 定义:把具有相同类型的若干变量按有序形式组织起来称为数组. C语言数组属于构造数据类型.一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型.因 ...

最新文章

  1. CSS Grid 网格布局全解析
  2. RHEL5 RHEL6升级GCC
  3. UVA 1613 K-Graph Oddity K度图着色 (构造)
  4. java8 java9 接口 interface
  5. Windows Phone 7中用好Silverlig“.NET研究”ht开发利器
  6. 直面PHP微服务架构挑战
  7. 数据结构-在O(1)时间删除链表节点
  8. 对ObjectMapper的理解及延伸
  9. 微软开发无人店技术叫阵Amazon Go,以后沃尔玛也能拿了就走?
  10. weblogic domain的安装与配置
  11. java double 初始化_java语言程序员之正确的初始化
  12. NRF52840开发注意
  13. 【力扣面试】面试题 04.02. 最小高度树(就是创建二叉平衡树)
  14. Java、JSP房产中介房屋信息管理系统
  15. STM32单片机bootloader扫盲
  16. 浏览器主页被hao123劫持之解决方案
  17. 联想计算机设置恢复出厂,联想电脑恢复出厂设置还原系统攻略
  18. 读《多头自注意力层和卷积层的关系》笔记
  19. php处理小米广告平台API上报方案对接(主要是APP下载)
  20. C# 淘宝商品微信返利助手开发-(二)返利助手开放文档以及帐号申请地址

热门文章

  1. 0622_ArcMap添加地图地图(矢量底图与影像地图)_太乐地图插件ArcTailer.tlb
  2. 一个神奇的分布式计算框架:jini
  3. 30系 显卡显存被占用又找不到进程的解决办法
  4. Qt解析XML相关(QDom方式)
  5. 蓝奏云 php 协议,蓝奏云网盘最新协议完整版附一套网络验证
  6. kafka(三)kafka steaming high-level api
  7. Datatable 列名
  8. 软件测试面试题:常见的性能测试方法有哪些?以及每类测试方法的目的是什么?
  9. 微信开通检测工具如何检测效果最好
  10. ICP备案和ICP许可证区别