Уже неделю я пытаюсь побороть артефакты с отображением большого количества кубов.
Коротко: есть данные, файл data.txt, который содержит информацию о размере кубов по y-координате. Отображается всё это через GPU Instancing.
Всё хорошо, кроме артефактов. Где-то в середине фрейма чёрная полоса и с 2ух боков ощущение, будто постоянно меняется очередь отрисовки у кубов, но это не точно. Похоже на проблемы с буфером глубины.
Пытался:
-Уменьшать масштаб кубов и уменьшать far и near plane камеры
-Вместо gpu instancing использовать ручное комбинирование мешей (CombineMeshes), записав цвета в вершины и истанс через drawmeshnow
-Вместо массива цветов использовать текстурную рампу
-Перепробовал все Blend моды в шейдере
-Все culling modes
-Отключал и вкючал буфер глубины разными способами
-Менял фруструм камеры (зачем-то)
-Подключал анти-алайзинг
Ничего не помогает. Не пробовал манипулировать буфером глубины (z-buffer), ибо на это моих скудных знаний не хватает.
На всякий случай версия Юнити 5.5.0f3
Видео с демонстрацией артефактов (фпс такой плохой ибо запись экрана + слабый ноутбук со встроенной видеокартой):
тестовый проект:
Скрытый текст:
GPU Instancing:
Синтаксис:
Используется csharp
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
public class BunchOfCubes : MonoBehaviour
{
public Mesh mesh; // mesh to draw (cube in this case)
public Material mat; //material to draw gpu instanced cubes
// public Vector2 size; // not used
// public Color color;
List<Matrix4x4> listMatrices;
public MatrixConteiner[] matConteiner; // container for matrices
// private List<Vector4> colors; // I used colors instead of texture ramp before. Same result.
private List<float> rampVals; //values for texture ramp
private List<MaterialPropertyBlock> blocks; // property blocks
void Start()
{
Generate();
}
public void Generate()
{
// mesh = GameObject.CreatePrimitive(PrimitiveType.Cube).GetComponent<MeshFilter>().mesh;
listMatrices = new List<Matrix4x4>();
//Props = new MaterialPropertyBlock();
// colors = new List<Vector4>();
rampVals = new List<float>();
blocks = new List<MaterialPropertyBlock>();
// for (int x = 0; x <= size.x; x++)
// {
// for (int y = 0; y < size.y; y++)
// {
// float rand = Random.Range(1f, 255f);
// Color col = Color.Lerp(Color.blue, Color.red, rand / 255f);
// // col = Color.red;
// colors.Add(col);
// listMatrices.Add(Matrix4x4.TRS(new Vector3(x, rand / 2, y), Quaternion.identity, new Vector3(1f, rand, 1f)));
// }
// }
float[,] data = DataReader.Inst.GetData();
for (int x = 0; x < data.GetLength(0); x++)
{
for (int y = 0; y < data.GetLength(1); y++)
{
// float scaleY = Random.Range(1f, 255f);
float scaleY = data[x,y];
// Color col = Color.Lerp(mat.GetColor("_Color0"), color, scaleY / 255f);
// colors.Add(col);
float rampVal = scaleY / 255f;
rampVals.Add(rampVal);
listMatrices.Add(Matrix4x4.TRS(new Vector3(x, scaleY / 2, y), Quaternion.identity, new Vector3(1f, scaleY, 1f)));
}
}
// simply split matrices and material blocks in chuncks of 1000
int len = listMatrices.Count / 1000;
if (listMatrices.Count % 1000 == 0)
matConteiner = new MatrixConteiner[len];
else
matConteiner = new MatrixConteiner[len + 1];
int q = 0;
for (int i = 0; i < listMatrices.Count; i++)
{
if ((float)(i) % 1000 == 0)
{
MaterialPropertyBlock prop = new MaterialPropertyBlock();
Matrix4x4[] mats;
int count = 0;
if (listMatrices.Count - i >= 1000) count = 1000;
else count = listMatrices.Count - i;
mats = new Matrix4x4[count];
mats = listMatrices.GetRange(i, count).ToArray();
matConteiner[q] = new MatrixConteiner(i, mats, count);
//prop.SetVectorArray("_Color", colors.GetRange(i, count));
prop.SetFloatArray("_RampVal", rampVals.GetRange(i, count));
blocks.Add(prop);
// matConteiner[i].matrix = mats;
// matConteiner[i].i = i;
// matConteiner[i].count = count;
q++;
}
}
//Props.SetVectorArray("_Color", colors);
//Initialize camera
Camera.main.GetComponent<GameCamera>().Init(new Vector3(data.GetLength(0) / 2, 0, data.GetLength(1) / 2));
}
void LateUpdate ()
{
for (int i = 0; i < matConteiner.Length; i++)
{
Graphics.DrawMeshInstanced(mesh, 0, mat, matConteiner[i].matrix, matConteiner[i].count, blocks[i], ShadowCastingMode.Off, false, 0);
}
}
public class MatrixConteiner
{
public int i;
public Matrix4x4[] matrix;
public int count;
public MatrixConteiner(int _i, Matrix4x4[] _matrix, int _count)
{
i = _i;
matrix = _matrix;
count = _count;
}
}
}
using UnityEngine;
using UnityEngine.Rendering;
public class BunchOfCubes : MonoBehaviour
{
public Mesh mesh; // mesh to draw (cube in this case)
public Material mat; //material to draw gpu instanced cubes
// public Vector2 size; // not used
// public Color color;
List<Matrix4x4> listMatrices;
public MatrixConteiner[] matConteiner; // container for matrices
// private List<Vector4> colors; // I used colors instead of texture ramp before. Same result.
private List<float> rampVals; //values for texture ramp
private List<MaterialPropertyBlock> blocks; // property blocks
void Start()
{
Generate();
}
public void Generate()
{
// mesh = GameObject.CreatePrimitive(PrimitiveType.Cube).GetComponent<MeshFilter>().mesh;
listMatrices = new List<Matrix4x4>();
//Props = new MaterialPropertyBlock();
// colors = new List<Vector4>();
rampVals = new List<float>();
blocks = new List<MaterialPropertyBlock>();
// for (int x = 0; x <= size.x; x++)
// {
// for (int y = 0; y < size.y; y++)
// {
// float rand = Random.Range(1f, 255f);
// Color col = Color.Lerp(Color.blue, Color.red, rand / 255f);
// // col = Color.red;
// colors.Add(col);
// listMatrices.Add(Matrix4x4.TRS(new Vector3(x, rand / 2, y), Quaternion.identity, new Vector3(1f, rand, 1f)));
// }
// }
float[,] data = DataReader.Inst.GetData();
for (int x = 0; x < data.GetLength(0); x++)
{
for (int y = 0; y < data.GetLength(1); y++)
{
// float scaleY = Random.Range(1f, 255f);
float scaleY = data[x,y];
// Color col = Color.Lerp(mat.GetColor("_Color0"), color, scaleY / 255f);
// colors.Add(col);
float rampVal = scaleY / 255f;
rampVals.Add(rampVal);
listMatrices.Add(Matrix4x4.TRS(new Vector3(x, scaleY / 2, y), Quaternion.identity, new Vector3(1f, scaleY, 1f)));
}
}
// simply split matrices and material blocks in chuncks of 1000
int len = listMatrices.Count / 1000;
if (listMatrices.Count % 1000 == 0)
matConteiner = new MatrixConteiner[len];
else
matConteiner = new MatrixConteiner[len + 1];
int q = 0;
for (int i = 0; i < listMatrices.Count; i++)
{
if ((float)(i) % 1000 == 0)
{
MaterialPropertyBlock prop = new MaterialPropertyBlock();
Matrix4x4[] mats;
int count = 0;
if (listMatrices.Count - i >= 1000) count = 1000;
else count = listMatrices.Count - i;
mats = new Matrix4x4[count];
mats = listMatrices.GetRange(i, count).ToArray();
matConteiner[q] = new MatrixConteiner(i, mats, count);
//prop.SetVectorArray("_Color", colors.GetRange(i, count));
prop.SetFloatArray("_RampVal", rampVals.GetRange(i, count));
blocks.Add(prop);
// matConteiner[i].matrix = mats;
// matConteiner[i].i = i;
// matConteiner[i].count = count;
q++;
}
}
//Props.SetVectorArray("_Color", colors);
//Initialize camera
Camera.main.GetComponent<GameCamera>().Init(new Vector3(data.GetLength(0) / 2, 0, data.GetLength(1) / 2));
}
void LateUpdate ()
{
for (int i = 0; i < matConteiner.Length; i++)
{
Graphics.DrawMeshInstanced(mesh, 0, mat, matConteiner[i].matrix, matConteiner[i].count, blocks[i], ShadowCastingMode.Off, false, 0);
}
}
public class MatrixConteiner
{
public int i;
public Matrix4x4[] matrix;
public int count;
public MatrixConteiner(int _i, Matrix4x4[] _matrix, int _count)
{
i = _i;
matrix = _matrix;
count = _count;
}
}
}
Shader:
Синтаксис:
Используется csharp
Shader "Custom/TextureRamp"
{
Properties
{
_Ramp("Texture ramp", 2D) = "white" {}
}
SubShader
{
//Tags { "RenderType"="Opaque" "Queue" = "Geometry" } //"UNITY_MAX_INSTANCE_COUNT"="150"
// LOD 100
Tags { "RenderType"="Opaque" "IgnoreProjector"="True" }
Pass
{
Cull Off
Blend One Zero
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// #pragma target 3.0
#pragma multi_compile_instancing
#include "UnityCG.cginc"
struct appdata
{
half4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
half4 vertex : SV_POSITION;
float4 col : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
uniform sampler2D _Ramp;
UNITY_INSTANCING_CBUFFER_START (Props)
UNITY_DEFINE_INSTANCED_PROP (fixed, _RampVal)
UNITY_INSTANCING_CBUFFER_END
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID (v);
UNITY_TRANSFER_INSTANCE_ID (v, o);
o.vertex = UnityObjectToClipPos (v.vertex);
// o.vertex = v.vertex;
o.col = tex2Dlod(_Ramp, float4(0, UNITY_ACCESS_INSTANCED_PROP(_RampVal), 0, 0));
//o.col = UNITY_ACCESS_INSTANCED_PROP (_Color); //lerp(_Color0, UNITY_ACCESS_INSTANCED_PROP (_Color), v.vertex.y + 0.5);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// fixed4 col = _Color0;
// UNITY_SETUP_INSTANCE_ID (i);
return i.col;
//return UNITY_ACCESS_INSTANCED_PROP (_Color);
}
ENDCG
}
}
}
{
Properties
{
_Ramp("Texture ramp", 2D) = "white" {}
}
SubShader
{
//Tags { "RenderType"="Opaque" "Queue" = "Geometry" } //"UNITY_MAX_INSTANCE_COUNT"="150"
// LOD 100
Tags { "RenderType"="Opaque" "IgnoreProjector"="True" }
Pass
{
Cull Off
Blend One Zero
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// #pragma target 3.0
#pragma multi_compile_instancing
#include "UnityCG.cginc"
struct appdata
{
half4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
half4 vertex : SV_POSITION;
float4 col : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
uniform sampler2D _Ramp;
UNITY_INSTANCING_CBUFFER_START (Props)
UNITY_DEFINE_INSTANCED_PROP (fixed, _RampVal)
UNITY_INSTANCING_CBUFFER_END
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID (v);
UNITY_TRANSFER_INSTANCE_ID (v, o);
o.vertex = UnityObjectToClipPos (v.vertex);
// o.vertex = v.vertex;
o.col = tex2Dlod(_Ramp, float4(0, UNITY_ACCESS_INSTANCED_PROP(_RampVal), 0, 0));
//o.col = UNITY_ACCESS_INSTANCED_PROP (_Color); //lerp(_Color0, UNITY_ACCESS_INSTANCED_PROP (_Color), v.vertex.y + 0.5);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// fixed4 col = _Color0;
// UNITY_SETUP_INSTANCE_ID (i);
return i.col;
//return UNITY_ACCESS_INSTANCED_PROP (_Color);
}
ENDCG
}
}
}
В чём может быть причина? Возможно, я просто туплю и ответ на поверхности.
Спасайте советом или напутствие, дорогие форумчане. Совсем встрял с этой задачей
Скрытый текст: