Constant buffer receives wrong value - c++

I have got this HLSL struct and when I pass in a Material buffer from C++ to HLSL, some values in my struct is wrong. I know this because I have tried for example setting the emissive color to Vector4(0, 1, 0, 1) or GREEN and in my pixel shader I return only the emissive color and the result becomes BLUE!
And when I set the emissive to (1, 0, 0, 1) or RED, the pixel shader outputs GREEN color. So it seems like everything is shifted 8 bytes to the right. What might be the reason behind this?
EDIT: I noticed that my virtual destructor made my structure bigger than usual. I removed that and then it worked!
HLSL struct
struct Material
{
float4 emissive;
float4 ambient;
float4 diffuse;
float4 specular;
float specularPower;
bool useTexture;
float2 padding;
};
C++ struct
class Material
{
virtual ~Material(); // - I removed this!
helium::Vector4 m_emissive;
helium::Vector4 m_ambient;
helium::Vector4 m_diffuse;
helium::Vector4 m_specular;
float m_specularPower;
int m_useTexture;
float m_padding[2];
};

Related

Converting Metal Shader texture type from 2D to 2D Multisample

I am following a tutorial by 2etime on YouTube about Apple's Metal graphics API. Everything works as intended, but when I try to change the view's sample count, I get a lot of errors. The errors, though, are easily fixed. So far, I've changed the sample count in every file, but I am stuck at the shaders. To change the view's sample count, you have to change the texture sample count (at least in my case), and to change the texture sample count, you have to change the texture type to multisample. This is easily done. However, after this change, I get errors from the shaders. It looks like the fragment shader uses texture2d, and I need to change it to texture2d_ms, but when I do so, I get error saying that 'sample' function is not valid. I will post the code and the error. I've tried searching on the internet, but I can't seem to find anything.
FinalShaders.metal
#include <metal_stdlib>
#include "Shared.metal"
using namespace metal;
struct FinalRasterizerData {
float4 position [[ position ]];
float2 textureCoordinate;
};
vertex FinalRasterizerData final_vertex_shader(const VertexIn vIn [[ stage_in ]]) {
FinalRasterizerData rd;
rd.position = float4(vIn.position, 1.0);
rd.textureCoordinate = vIn.textureCoordinate;
return rd;
}
fragment half4 final_fragment_shader(const FinalRasterizerData rd [[ stage_in ]],
texture2d_ms<float> baseTexture) {
sampler s;
float2 textureCoordinate = rd.textureCoordinate;
textureCoordinate.y = 1 - textureCoordinate.y;
float4 color = baseTexture.sample(s, textureCoordinate);
return half4(color);
}
Shared.metal
#ifndef SHARED_METAL
#define SHARED_METAL
#include <metal_stdlib>
using namespace metal;
struct VertexIn {
float3 position [[ attribute(0) ]];
float4 color [[ attribute(1) ]];
float2 textureCoordinate [[ attribute(2) ]];
float3 normal [[ attribute(3) ]];
float3 tangent [[ attribute(4) ]];
float3 bitangent [[ attribute(5) ]];
};
struct RasterizerData{
float4 position [[ position ]];
float4 color;
float2 textureCoordinate;
float totalGameTime;
float3 worldPosition;
float3 toCameraVector;
float3 surfaceNormal;
float3 surfaceTangent;
float3 surfaceBitangent;
};
struct ModelConstants{
float4x4 modelMatrix;
};
struct SceneConstants{
float totalGameTime;
float4x4 viewMatrix;
float4x4 skyViewMatrix;
float4x4 projectionMatrix;
float3 cameraPosition;
};
struct Material {
float4 color;
bool isLit;
bool useBaseTexture;
bool useNormalMapTexture;
float3 ambient;
float3 diffuse;
float3 specular;
float shininess;
};
struct LightData {
float3 position;
float3 color;
float brightness;
float ambientIntensity;
float diffuseIntensity;
float specularIntensity;
};
#endif
And the error:
No member named 'sample' in 'metal::texture2d_ms<float, metal::access::read, void>'
I know it clearly says that 'sample' is not existent, but I can't find any solution. When I removed it, and replaced the return with different value, I only see a lot of green/yellow lines, and no 3d objects. Thanks for any help!
(the shader the error is happening in is the fragment one in final shaders.metal)
Sampling in a way that sample function means doesn't make sense for multisampled textures. What you are looking for instead is reading the exact sample value at a given texcoord.
If you look in the Metal Shader Language specification, in section 6.12.8 it describes which functions exist for 2D multisampled textures.
Those include:
Tv read(uint2 coord, uint sample) const
Tv read(ushort2 coord, ushort sample) const
uint get_width() const
uint get_height() const
uint get_num_samples() const
In read function, coord means pixel coordinates, so they would go from 0 to get_width() on X axis and from 0 to get_height() on Y axis. sample is the index of the sample, so it would be from 0 to get_num_samples()

How to pass a custom struct from vertex shader to fragment shader

In the fragment shader, I defined two structures as follows
struct DirLight{
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
struct PointLight {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
and in vertex shader, I defined the following variables, because I first want to do some transformations (like matrix multiplication that is not recommended in the fragment shader) on these uniform variables in the vertex shader.
uniform DirLight dirLight; // only one directional light
uniform int pointLightCnt; // number of point light sources
uniform PointLight pointLight[MAX]; // point lights
What should I do to transfer the structure in the vertex shader to the fragment shader?
Can I use a method similar to c++ like:
Define the structure in the header file, include them in both the vertex shader and the fragment shader, then define the corresponding out variable in the vertex shader, and define the corresponding in variable in the fragment shader to achieve it?
I was going to go into a long explanation of how to implement your lighting structure so it is generic to any light type, but that is a separate issue.
Your current issue is that the Vertex Function shouldn't need to use the lighting uniform at all; there's no data to pass between them. The only thing the Vertex shader should be doing is converting the local space to clip space and saving the intermediate world space as a separate part of the fragment shader's input so it can calculate the lighting properly.
All the lighting calculations can be done on the pixel/fragment shader and any dynamic lighting (positions, penumbra calculations, direction changes, etc) should be done on the CPU and just passed on to the GPU in the lighting buffer/uniform all at once.
This is in hlsl, but it's easily converted to glsl:
//Uniform
cbuffer matrix_cb : register(b0) {
float4x4 g_MODEL;
float4x4 g_VIEW;
float4x4 g_PROJECTION;
};
struct vs_in_t {
float3 position : POSITION;
float4 color : COLOR;
float2 uv : UV;
float4 normal : NORMAL;
};
struct ps_in_t {
float4 position : SV_POSITION;
float4 color : COLOR;
float2 uv : UV;
float4 normal : NORMAL;
float3 world_position : WORLD;
};
ps_in_t VertexFunction(vs_in_t input_vertex) {
ps_in_t output;
float4 local = float4(input_vertex.position, 1.0f);
float4 normal = input_vertex.normal;
float4 world = mul(local, g_MODEL);
float4 view = mul(world, g_VIEW);
float4 clip = mul(view, g_PROJECTION);
output.position = clip;
output.color = input_vertex.color;
output.uv = input_vertex.uv;
output.normal = normal;
output.world_position = world.xyz;
return output;
}

storage buffer and wrongfully aligned data

I'm giving some materials to my storage buffer so that my shader can have the materials of the objects I'm trying to draw, but the colors do no correspond, I think it's due to memory alignment but I'm new to opengl so I don't find the error
struct Material
{
Color diffuse;
Color specular;
Color emission;
float ns;
Material( ) : diffuse(0.8f, 0.8f, 0.8f), specular(Black()), emission(), ns(0) {}
};
struct Color
{
float r, g, b, a;
};
and my fragment shader
struct Material
{
vec4 diffuse;
vec4 specular;
vec4 emission;
float ns;
};
layout(binding=1) readonly buffer IndexBlock{
uint color_indices[];
};
layout(binding=2) readonly buffer MaterialBlock {
Material materials[];
};
in the main :
uint color_index = color_indices[gl_PrimitiveID];
vec3 frag_color = materials[color_index].diffuse.xyz;
I recommend to use std140 or std430 layout qualifier.
See OpenGL 4.6 API Core Profile Specification; 7.6.2.2 Standard Uniform Block Layout:
[...]
If the member is a structure, the base alignment of the structure is N, where
N is the largest base alignment value of any of its members, [...]
If the member is an array of S structures, the S elements of the array are laid out in order, according to rule (9).
You can add 3 floats to the c++ structure. But it It would be better to use an alignas specifier and align the structure to 16 bytes:
struct alignas(16) Material
{
Color diffuse;
Color specular;
Color emission;
float ns;
// [...]
}
Shader:
struct Material
{
vec4 diffuse;
vec4 specular;
vec4 emission;
float ns;
};
layout(binding=1, std430) readonly buffer IndexBlock{
uint color_indices[];
};
layout(binding=2, std430) readonly buffer MaterialBlock {
Material materials[];
};
The difference between std140 and std430 is, that for std140 the base alignment and stride of arrays of scalars and vectors and of structures is rounded up to a multiple of the base alignment of a vec4. This is not the case for std430.
You can test if Color matches vec4 easily.
std::cout<< sizeof(Color) <<std::endl;
std::cout<< sizeof(vec4) <<std::endl;
if the sizes are different then you can stop here.
if the sizes are equal, then try to init the same values for Color and vec4
Color c(1.0,2.0,3.0,4.0);
vec4 v(1.0,2.0,3.0,4.0);
unsigned char buf1[sizeof(Color)];
unsigned char buf2[sizeof(vec4)];
memcpy(buf1, &c, sizeof(Color));
memcpy(buf2, &v, sizeof(vec4));
bool equal = (memcmp(buf1, buf2, sizeof(Color)) == 0);
std::cout<< "Is equal? " << equal ? "yes" : "no" <<std::endl;
BTW: I think vec4 uses double, not float.

GLSL - unable to access second index of a SSBO array for multiple lights

In my application I add two lights. One at (0,0,2) and the second one at (2,0,0). Here's what I get (the x,y,z axes are represented respectively by the red, green & blue lines):
Notice how only the first light is working and the second is not. I made my application core-profile compliant to inspect the buffers with various tools like RenderDoc and NSight and both show me that the second light's data is present in the buffer (picture taken while running Nsight):
The positions seem to be correctly transfered to the gpu memory buffer. Here's the implementation of my fragment shader that uses a SSBO to handle multiple lights in my application:
#version 430
struct Light {
vec3 position;
vec3 color;
float intensity;
float attenuation;
float radius;
};
layout (std140, binding = 0) uniform CameraInfo {
mat4 ProjectionView;
vec3 eye;
};
layout (std430, binding = 1) readonly buffer LightsData {
Light lights[];
};
uniform vec3 ambient_light_color;
uniform float ambient_light_intensity;
in vec3 ex_FragPos;
in vec4 ex_Color;
in vec3 ex_Normal;
out vec4 out_Color;
void main(void)
{
// Basic ambient light
vec3 ambient_light = ambient_light_color * ambient_light_intensity;
int i;
vec3 diffuse = vec3(0.0,0.0,0.0);
vec3 specular = vec3(0.0,0.0,0.0);
for (i = 0; i < lights.length(); ++i) {
Light wLight = lights[i];
// Basic diffuse light
vec3 norm = normalize(ex_Normal); // in this project the normals are all normalized anyway...
vec3 lightDir = normalize(wLight.position - ex_FragPos);
float diff = max(dot(norm, lightDir), 0.0);
diffuse += diff * wLight.color;
// Basic specular light
vec3 viewDir = normalize(eye - ex_FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
specular += wLight.intensity * spec * wLight.color;
}
out_Color = ex_Color * vec4(specular + diffuse + ambient_light,1.0);
}
Note that I've read the section 7.6.2.2 of the OpenGL 4.5 spec and that, if I understood correctly, my alignment should follow the size of the biggest member of my struct, which is a vec3 and my struct size is 36 bytes so everything should be fine here. I also tried different std version (e.g. std140) and adding some padding, but nothing fixes the issue with the second light. In my C++ code, I have those definitions to add the lights in my application:
light_module.h/.cc:
struct Light {
glm::f32vec3 position;
glm::f32vec3 color;
float intensity;
float attenuation;
float radius;
};
...
constexpr GLuint LIGHTS_SSBO_BINDING_POINT = 1U;
std::vector<Light> _Lights;
...
void AddLight(const Light &light) {
// Add to _Lights
_Lights.push_back(light);
UpdateSSBOBlockData(
LIGHTS_SSBO_BINDING_POINT, _Lights.size()* sizeof(Light),
static_cast<void*>(_Lights.data()), GL_DYNAMIC_DRAW);
}
shader_module.h/.cc:
using SSBOCapacity = GLuint;
using BindingPoint = GLuint;
using ID = GLuint;
std::map<BindingPoint, std::pair<ID, SSBOCapacity> > SSBO_list;
...
void UpdateSSBOBlockData(GLuint a_unBindingPoint,
GLuint a_unSSBOSize, void* a_pData, GLenum a_eUsage) {
auto SSBO = SSBO_list.find(a_unBindingPoint);
if (SSBO != SSBO_list.end()) {
GLuint unSSBOID = SSBO->second.first;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, unSSBOID);
glBufferData(GL_SHADER_STORAGE_BUFFER, a_unSSBOSize, a_pData, a_eUsage);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); //unbind
}
else
// error handling...
}
Basically, I'm trying to update/reallocate the SSBO size with glBufferData each time a light is added in my app.
Now, since I'm having issues processing the second light data, I changed my fragment shader code to only execute the second light in my SSBO array by forcing i = 1 and looping until i < 2, but I get the following errors:
(50) : error C1068: ... or possible array index out of bounds
(50) : error C5025: lvalue in field access too complex
(56) : error C1068: ... or possible array index out of bounds
(56) : error C5025: lvalue in field access too complex
Lines 50 and 56 refer to diffuse += diff * wLight.color; and specular += wLight.intensity * spec * wLight.color; respectively. Is there really an out of bounds access even if I add my lights before the first draw call? Why is the shader compiling correctly when I'm using lights.length() instead of 2?
Finally, I've added a simple if (i == 1) in my for-loop to see if lights.length() is equal to 2, but it doesn't go in it. Yet the initial size of my buffer is 0 and then I add a light that sets the buffer size to 36 bytes and we can see that the first light works fine. Why is the update/reallocate not working the second time?
So what I did was to add some padding at the end of the declaration of my struct on the C++ side only. The padding required was float[3] or 12 bytes, which sums up to 48 bytes. I'm still not sure why this is required, since the specifications state (as highlighted in this post)
If the member is a structure, the base alignment of the structure is N, where N is the largest base alignment value of any of its
members, and rounded up to the base alignment of a vec4. The
individual members of this sub-structure are then assigned offsets by
applying this set of rules recursively, where the base offset of the
first member of the sub-structure is equal to the aligned offset of
the structure. The structure may have padding at the end; the base
offset of the member following the sub-structure is rounded up to the
next multiple of the base alignment of the structure.
[...]
When using the std430 storage layout, shader storage blocks will be
laid out in buffer storage identically to uniform and shader storage
blocks using the std140 layout, except that the base alignment and
stride of arrays of scalars and vectors in rule 4 and of structures in
rule 9 are not rounded up a multiple of the base alignment of a vec4.
My guess is that structures such as vec3 and glm::f32vec3 defined by glm are recursively rounded up to vec4 when using std430 and therefore my struct must follow the alignment of a vec4. If anyone can confirm this, it would be interesting since the linked post above deals with vec4 directly and not vec3.
Picture with both lights working :
EDIT:
After more investigation, it turns out that the last 3 fields of the Light struct (intensity, attenuation and radius) were not usable. I fixed this by changing the position and color from glm::f32vec3 to glm::vec4 instead. More information can be found in a similar post. I also left a single float for padding, because of the alignment mentioned earlier.

Pixel Shader Constant Buffer alignment

I have a pixel shader buffer
cbuffer InputBuffer {
float fTextureWidth;
float fTextureHeight;
float fTimeStep;
float padding; //padding to align to 16 bytes
};
That corresponds to a struct in my shader class:
struct InputBuffer {
float fTextureWidth;
float fTextureHeight;
float fTimeStep;
float padding;
};
Which looks all fine, since its 16-byte aligned. But when I render I get this warning:
The size of the Constant Buffer at slot 0 of the Pixel Shader unit is too small (16 bytes provided, 32 bytes, at least, expected). This is OK, as out-of-bounds reads are defined to return 0. It is also possible the developer knows the missing data will not be used anyway. This is only a problem if the developer actually intended to bind a sufficiently large Constant Buffer for what the shader expects. [ EXECUTION WARNING #351: DEVICE_DRAW_CONSTANT_BUFFER_TOO_SMALL]
Why would the buffer expect 32 bytes? I thought 16 was the minimum and mine clearly is?
EDIT 1: I've added the output of D3DReflect and my shader code.
After running D3DReflect on the pixel shader and retrieving a ID3D11ShaderReflectionConstantBuffer pointer to the constant buffer I ran the GetDesc method on the buffer and got what I expected:
Type: D3D_CT_BUFFER(0)
Variables: 4
Size: 16
Flags: 0
As requested, here is the pixel shader code:
/////////////
// GLOBALS //
/////////////
Texture2D shaderTextures[2]; // 0 is the velocity field texture and 1 is the field that is to be advected
SamplerState SampleType;
/////////////
// BUFFERS //
/////////////
cbuffer InputBuffer {
float fTextureWidth;
float fTextureHeight;
float fTimeStep;
float fDissipation;
};
//////////////
// TYPEDEFS //
//////////////
struct PixelInputType {
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
// Pixel Shader
float4 AdvectionPixelShader(PixelInputType input) : SV_TARGET {
float2 u = shaderTextures[0].Sample(SampleType, input.tex).xy; // velocity
float2 rdFactors = float2(1.0f/fTextureWidth,1.0f/fTextureHeight);
float2 prevPos = float2(input.tex.x - (fTimeStep*rdFactors.x*u.x),input.tex.y - (fTimeStep*rdFactors.y*u.y) );
float2 final = shaderTextures[1].Sample(SampleType,prevPos).xy;
return float4(final,0.0f,1.0f)*fDissipation;
}