问题描述
我正在编写有关在WGPU(Vulkan GLSL)计算着色器中计算切线和切线的教程。我正在使用在Blender中创建的.obj在cpu上创建顶点缓冲区。
这是计算着色器的代码。
#version 450
#define VERTICES_PER_TRIANGLE 3
layout(local_size_x = VERTICES_PER_TRIANGLE) in;
// Should match the struct in model.rs
struct ModelVertex {
vec3 position;
vec2 tex_coords;
vec3 normal;
vec3 tangent;
vec3 bitangent;
};
layout(std140,set=0,binding=0) buffer SrcVertexBuffer {
ModelVertex srcVertices[];
};
layout(std140,binding=1) buffer DstVertexBuffer {
ModelVertex dstVertices[];
};
layout(std140,binding=2) buffer IndexBuffer {
uint Indices[];
};
void main() {
uint index = gl_GlobalInvocationID.x;
// Grab the indices for the triangle
uint i0 = Indices[index];
uint i1 = Indices[index + 1];
uint i2 = Indices[index + 2];
// Grab the vertices for the triangle
ModelVertex v0 = srcVertices[i0];
ModelVertex v1 = srcVertices[i1];
ModelVertex v2 = srcVertices[i2];
// Grab the position and uv components of the vertices
vec3 pos0 = v0.position;
vec3 pos1 = v1.position;
vec3 pos2 = v2.position;
vec2 uv0 = v0.tex_coords;
vec2 uv1 = v1.tex_coords;
vec2 uv2 = v2.tex_coords;
// Calculate the edges of the triangle
vec3 delta_pos1 = pos1 - pos0;
vec3 delta_pos2 = pos2 - pos0;
// This will give us a direction to calculate the
// tangent and bitangent
vec2 delta_uv1 = uv1 - uv0;
vec2 delta_uv2 = uv2 - uv0;
// Solving the following system of equations will
// give us the tangent and bitangent.
// delta_pos1 = delta_uv1.x * T + delta_u.y * B
// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B
// Luckily,the place I found this equation provided
// the solution!
float r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
vec3 tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
vec3 bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r;
// We'll use the same tangent/bitangent for each vertex in the triangle
dstVertices[i0].tangent = tangent;
dstVertices[i1].tangent = tangent;
dstVertices[i2].tangent = tangent;
dstVertices[i0].bitangent = bitangent;
dstVertices[i1].bitangent = bitangent;
dstVertices[i2].bitangent = bitangent;
}
这将导致如下图像。
问题出现在最后六行。
dstVertices[i0].tangent = tangent;
dstVertices[i1].tangent = tangent;
dstVertices[i2].tangent = tangent;
dstVertices[i0].bitangent = bitangent;
dstVertices[i1].bitangent = bitangent;
dstVertices[i2].bitangent = bitangent;
如果我删除这些行,则输出很好(尽管由于切线和bitangent为0向量,所以照明全部错误)。
为什么要修改顶点的位置的切线和双切线?
这是上下文的其余代码。 https://github.com/sotrh/learn-wgpu/tree/compute/code/intermediate/tutorial14-compute
编辑:
let src_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer",m.name)),contents: bytemuck::cast_slice(&vertices),// UPDATED!
usage: wgpu::BufferUsage::STORAGE,});
let dst_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer",// UPDATED!
usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::STORAGE,});
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer",contents: bytemuck::cast_slice(&m.mesh.indices),// UPDATED!
usage: wgpu::BufferUsage::INDEX | wgpu::BufferUsage::STORAGE,});
let binding = BitangentComputeBinding {
dst_vertex_buffer,src_vertex_buffer,index_buffer,num_elements: m.mesh.indices.len() as u32,};
// Calculate the tangents and bitangents
let calc_bind_group = self.binder.create_bind_group(
&binding,device,Some("Mesh BindGroup")
);
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Tangent and Bitangent Calc"),});
{
let mut pass = encoder.begin_compute_pass();
pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0,&calc_bind_group,&[]);
pass.dispatch(binding.num_elements as u32 / 3,1,1);
}
queue.submit(std::iter::once(encoder.finish()));
device.poll(wgpu::Maintain::Wait);
着色器应该遍历网格中的所有三角形,并使用该三角形的顶点的正子和uv坐标计算切线和双切线。我猜测与多个三角形共享的顶点将同时写入,从而导致此内存损坏。
我认为其他地方的着色器没有问题,因为我对光源使用相同的模型,而负责该着色器的顶点着色器根本不使用切线和切线。
#version 450
layout(location=0) in vec3 a_position;
layout(location=0) out vec3 v_color;
layout(set=0,binding=0)
uniform Uniforms {
vec3 u_view_position;
mat4 u_view_proj;
};
layout(set=1,binding=0)
uniform Light {
vec3 u_position;
vec3 u_color;
};
// Let's keep our light smaller than our other objects
float scale = 0.25;
void main() {
vec3 v_position = a_position * scale + u_position;
gl_Position = u_view_proj * vec4(v_position,1);
v_color = u_color;
}
查看“渲染文档”中的顶点数据表明它们的位置数据变得混乱。
如果我将切线和切线设置为vec3(0,0)
,那么多维数据集也是这样。
我唯一的猜测是存储缓冲区具有我不知道的字节对齐规则。我知道统一缓冲区就是这种情况,但是我正在为我的实例化代码使用存储缓冲区,而且似乎没有任何问题。
解决方法
当使用std430
时,Vulkan样式GLSL会与结构中的最大字段对齐。
https://github.com/KhronosGroup/glslang/issues/264
在我的情况下为vec3
。 vec2 tex_coord
将其丢弃,导致着色器从顶点缓冲区的错误部分提取数据。
解决方法是更改struct
中的model_load.comp
,以指定各个组件。
struct ModelVertex {
float x; float y; float z;
float uv; float uw;
float nx; float ny; float nz;
float tx; float ty; float tz;
float bx; float by; float bz;
};
现在基本对齐方式是float
(4个字节),并且着色器可以正确读取顶点缓冲区数据。
我知道这里有一个packed
布局,但是shaderc
不允许我出于其他原因使用它。老实说,我认为这很烦人,而且很麻烦,但是可以。
结果仍然存在缺陷。立方体的边缘上有一些条纹。我的猜测是,它是一个共享多个三角形的顶点,但这是我以后需要研究的另一个问题。