Why does order matter in shaders?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
A Quick Note
This question has the C++
tag because there are more developers working with DirectX
in C++
than there are in C#
. I don't believe this question is directly related to either language, but instead to the types used (which as I understand it are exactly the same), or DirectX
itself and how it compiles shaders. If someone working in C++
knows a better and more descriptive answer, then I would prefer that over my own answer. I understand both languages but use C#
primarily.
Overview
In an HLSL
shader, when setting up my constant buffers I ran into an issue that was rather peculiar. The original constant buffers in question were set up as follows:
cbuffer ObjectBuffer : register(b0) {
float4x4 WorldViewProjection;
float4x4 World;
float4x4 WorldInverseTranspose;
}
cbuffer ViewBuffer : register(b1) {
DirectionalLight Light;
float3 CameraPosition;
float3 CameraUp;
float2 RenderTargetSize;
}
If I swap the b0
and b1
registers around, rendering no longer works (e1). If I leave those registers alone, and swap the order between World
and WorldViewProjection
again, rendering no longer works (e2). However, simply moving the ViewBuffer
above the ObjectBuffer
in the HLSL
file without making other modifications, it works just fine.
Now, I expect the register placement is rather important and that the first register b0
requires the three properties given in that buffer, and I understand that HLSL
constant buffers are required to be in 16 byte chunks. However, this leaves me with some questions.
Questions
Given the fact that HLSL
expects constant buffers to be in 16 byte chunks;
- Why does the ordering in e2 matter so much?
Aren't float4x4
types the same as Matrix
types where it's essentially an array of arrays?
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ TOTAL ] = 64 bytes
Since a float
is 4 bytes on its own, this would mean a float4
is 16 bytes, and thus a float4x4
is 64 bytes. So why does the order matter if the size remained the same?
- Why does the
ObjectBuffer
have to be assigned tob0
in this case instead of any otherb
register?
c# c++ directx-11 hlsl sharpdx
add a comment |
A Quick Note
This question has the C++
tag because there are more developers working with DirectX
in C++
than there are in C#
. I don't believe this question is directly related to either language, but instead to the types used (which as I understand it are exactly the same), or DirectX
itself and how it compiles shaders. If someone working in C++
knows a better and more descriptive answer, then I would prefer that over my own answer. I understand both languages but use C#
primarily.
Overview
In an HLSL
shader, when setting up my constant buffers I ran into an issue that was rather peculiar. The original constant buffers in question were set up as follows:
cbuffer ObjectBuffer : register(b0) {
float4x4 WorldViewProjection;
float4x4 World;
float4x4 WorldInverseTranspose;
}
cbuffer ViewBuffer : register(b1) {
DirectionalLight Light;
float3 CameraPosition;
float3 CameraUp;
float2 RenderTargetSize;
}
If I swap the b0
and b1
registers around, rendering no longer works (e1). If I leave those registers alone, and swap the order between World
and WorldViewProjection
again, rendering no longer works (e2). However, simply moving the ViewBuffer
above the ObjectBuffer
in the HLSL
file without making other modifications, it works just fine.
Now, I expect the register placement is rather important and that the first register b0
requires the three properties given in that buffer, and I understand that HLSL
constant buffers are required to be in 16 byte chunks. However, this leaves me with some questions.
Questions
Given the fact that HLSL
expects constant buffers to be in 16 byte chunks;
- Why does the ordering in e2 matter so much?
Aren't float4x4
types the same as Matrix
types where it's essentially an array of arrays?
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ TOTAL ] = 64 bytes
Since a float
is 4 bytes on its own, this would mean a float4
is 16 bytes, and thus a float4x4
is 64 bytes. So why does the order matter if the size remained the same?
- Why does the
ObjectBuffer
have to be assigned tob0
in this case instead of any otherb
register?
c# c++ directx-11 hlsl sharpdx
add a comment |
A Quick Note
This question has the C++
tag because there are more developers working with DirectX
in C++
than there are in C#
. I don't believe this question is directly related to either language, but instead to the types used (which as I understand it are exactly the same), or DirectX
itself and how it compiles shaders. If someone working in C++
knows a better and more descriptive answer, then I would prefer that over my own answer. I understand both languages but use C#
primarily.
Overview
In an HLSL
shader, when setting up my constant buffers I ran into an issue that was rather peculiar. The original constant buffers in question were set up as follows:
cbuffer ObjectBuffer : register(b0) {
float4x4 WorldViewProjection;
float4x4 World;
float4x4 WorldInverseTranspose;
}
cbuffer ViewBuffer : register(b1) {
DirectionalLight Light;
float3 CameraPosition;
float3 CameraUp;
float2 RenderTargetSize;
}
If I swap the b0
and b1
registers around, rendering no longer works (e1). If I leave those registers alone, and swap the order between World
and WorldViewProjection
again, rendering no longer works (e2). However, simply moving the ViewBuffer
above the ObjectBuffer
in the HLSL
file without making other modifications, it works just fine.
Now, I expect the register placement is rather important and that the first register b0
requires the three properties given in that buffer, and I understand that HLSL
constant buffers are required to be in 16 byte chunks. However, this leaves me with some questions.
Questions
Given the fact that HLSL
expects constant buffers to be in 16 byte chunks;
- Why does the ordering in e2 matter so much?
Aren't float4x4
types the same as Matrix
types where it's essentially an array of arrays?
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ TOTAL ] = 64 bytes
Since a float
is 4 bytes on its own, this would mean a float4
is 16 bytes, and thus a float4x4
is 64 bytes. So why does the order matter if the size remained the same?
- Why does the
ObjectBuffer
have to be assigned tob0
in this case instead of any otherb
register?
c# c++ directx-11 hlsl sharpdx
A Quick Note
This question has the C++
tag because there are more developers working with DirectX
in C++
than there are in C#
. I don't believe this question is directly related to either language, but instead to the types used (which as I understand it are exactly the same), or DirectX
itself and how it compiles shaders. If someone working in C++
knows a better and more descriptive answer, then I would prefer that over my own answer. I understand both languages but use C#
primarily.
Overview
In an HLSL
shader, when setting up my constant buffers I ran into an issue that was rather peculiar. The original constant buffers in question were set up as follows:
cbuffer ObjectBuffer : register(b0) {
float4x4 WorldViewProjection;
float4x4 World;
float4x4 WorldInverseTranspose;
}
cbuffer ViewBuffer : register(b1) {
DirectionalLight Light;
float3 CameraPosition;
float3 CameraUp;
float2 RenderTargetSize;
}
If I swap the b0
and b1
registers around, rendering no longer works (e1). If I leave those registers alone, and swap the order between World
and WorldViewProjection
again, rendering no longer works (e2). However, simply moving the ViewBuffer
above the ObjectBuffer
in the HLSL
file without making other modifications, it works just fine.
Now, I expect the register placement is rather important and that the first register b0
requires the three properties given in that buffer, and I understand that HLSL
constant buffers are required to be in 16 byte chunks. However, this leaves me with some questions.
Questions
Given the fact that HLSL
expects constant buffers to be in 16 byte chunks;
- Why does the ordering in e2 matter so much?
Aren't float4x4
types the same as Matrix
types where it's essentially an array of arrays?
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ 0, 0, 0, 0 ] = 16 bytes
[ TOTAL ] = 64 bytes
Since a float
is 4 bytes on its own, this would mean a float4
is 16 bytes, and thus a float4x4
is 64 bytes. So why does the order matter if the size remained the same?
- Why does the
ObjectBuffer
have to be assigned tob0
in this case instead of any otherb
register?
c# c++ directx-11 hlsl sharpdx
c# c++ directx-11 hlsl sharpdx
edited Nov 19 '18 at 14:19
PerpetualJ
asked Nov 16 '18 at 20:44
PerpetualJPerpetualJ
863220
863220
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
A Quick Note
I am currently working on further analysis of the problem so that I can give a more detailed and accurate answer. I will update the question and answer to reflect as much accuracy as possible as I discover more details.
Basic Answer
The exact issue with the question above (which was unknown at the time of posting), is that the HLSL
buffers didn't match their C#
representations; thus the reordering of variables caused the shader to fail. However, I am still unsure as to why when the types are the same. I have learned of some other things along my road for an answer and have decided to post them here.
Why Order Matters
After some further research and testing, I'm still not 100% sure as to the reasoning behind this where types are all the same. Overall, I believe it may be due to the expected types in the cbuffer
and the order of the types in the struct
. In this case, if your cbuffer
expects a bool
first and then a float
, then rearranging causes issues.
cbuffer MaterialBuffer : register(b0) {
bool HasTexture;
float SpecularPower;
float4 Ambient;
...
}
// Won't work.
public struct MaterialBuffer {
public float SpecularPower;
public Vector2 padding2;
public bool HasTexture;
private bool padding0;
private short padding1;
public Color4 Ambient;
...
}
// Works.
public struct MaterialBuffer {
public bool HasTexture;
private bool padding0;
private short padding1;
public float SpecularPower;
public Vector2 padding2;
public Color4 Ambient;
...
}
I put some research effort into testing the differences in byte size of types and this didn't really seem to change much of anything but I'll post my findings for common basic types here:
1 Byte : bool, sbyte, byte
2 Bytes : short, ushort
4 Bytes : int, uint, float
8 Bytes : long, ulong, double
16 Bytes: decimal
You do have to be conscious of the basic types used to construct more complex types. Say for example you have a Vector2
with an X
property and a Y
property. If those are represented by float
types then you'll need an 8 byte padding prior to the next property unless you have some other stuff to help reach 16 bytes. However, if those are represented by double
types or decimal
types then the size is different and you'll need to be aware of that.
Register Assignments
I was able to solve the register issue; this also corresponds to the C#
side when you set the buffers. As you set the buffers, you assign indices to those buffers and the HLSL
is expected to use the same indices.
// Buffer declarations in HLSL.
cbuffer ViewBuffer : register(b0)
cbuffer CameraBuffer : register(b1);
cbuffer MaterialBuffer : register(b2);
// Buffer assignments in C#.
context.VertexShader.SetConstantBuffer(0, viewBuffer);
context.VertexShader.SetConstantBuffer(1, cameraBuffer);
context.VertexShader.SetConstantBuffer(2, materialBuffer);
The above code will work as expected since the buffers are assigned to the correct registers. However, if we change the buffer for the camera to 8 for example, then the cbuffer
would have to be assigned to register b8
in order to work properly. The code below doesn't work for that exact reason.
cbuffer CameraBuffer : register(b1)
context.VertexShader.SetConstantBuffer(8, cameraBuffer);
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53345125%2fwhy-does-order-matter-in-shaders%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
A Quick Note
I am currently working on further analysis of the problem so that I can give a more detailed and accurate answer. I will update the question and answer to reflect as much accuracy as possible as I discover more details.
Basic Answer
The exact issue with the question above (which was unknown at the time of posting), is that the HLSL
buffers didn't match their C#
representations; thus the reordering of variables caused the shader to fail. However, I am still unsure as to why when the types are the same. I have learned of some other things along my road for an answer and have decided to post them here.
Why Order Matters
After some further research and testing, I'm still not 100% sure as to the reasoning behind this where types are all the same. Overall, I believe it may be due to the expected types in the cbuffer
and the order of the types in the struct
. In this case, if your cbuffer
expects a bool
first and then a float
, then rearranging causes issues.
cbuffer MaterialBuffer : register(b0) {
bool HasTexture;
float SpecularPower;
float4 Ambient;
...
}
// Won't work.
public struct MaterialBuffer {
public float SpecularPower;
public Vector2 padding2;
public bool HasTexture;
private bool padding0;
private short padding1;
public Color4 Ambient;
...
}
// Works.
public struct MaterialBuffer {
public bool HasTexture;
private bool padding0;
private short padding1;
public float SpecularPower;
public Vector2 padding2;
public Color4 Ambient;
...
}
I put some research effort into testing the differences in byte size of types and this didn't really seem to change much of anything but I'll post my findings for common basic types here:
1 Byte : bool, sbyte, byte
2 Bytes : short, ushort
4 Bytes : int, uint, float
8 Bytes : long, ulong, double
16 Bytes: decimal
You do have to be conscious of the basic types used to construct more complex types. Say for example you have a Vector2
with an X
property and a Y
property. If those are represented by float
types then you'll need an 8 byte padding prior to the next property unless you have some other stuff to help reach 16 bytes. However, if those are represented by double
types or decimal
types then the size is different and you'll need to be aware of that.
Register Assignments
I was able to solve the register issue; this also corresponds to the C#
side when you set the buffers. As you set the buffers, you assign indices to those buffers and the HLSL
is expected to use the same indices.
// Buffer declarations in HLSL.
cbuffer ViewBuffer : register(b0)
cbuffer CameraBuffer : register(b1);
cbuffer MaterialBuffer : register(b2);
// Buffer assignments in C#.
context.VertexShader.SetConstantBuffer(0, viewBuffer);
context.VertexShader.SetConstantBuffer(1, cameraBuffer);
context.VertexShader.SetConstantBuffer(2, materialBuffer);
The above code will work as expected since the buffers are assigned to the correct registers. However, if we change the buffer for the camera to 8 for example, then the cbuffer
would have to be assigned to register b8
in order to work properly. The code below doesn't work for that exact reason.
cbuffer CameraBuffer : register(b1)
context.VertexShader.SetConstantBuffer(8, cameraBuffer);
add a comment |
A Quick Note
I am currently working on further analysis of the problem so that I can give a more detailed and accurate answer. I will update the question and answer to reflect as much accuracy as possible as I discover more details.
Basic Answer
The exact issue with the question above (which was unknown at the time of posting), is that the HLSL
buffers didn't match their C#
representations; thus the reordering of variables caused the shader to fail. However, I am still unsure as to why when the types are the same. I have learned of some other things along my road for an answer and have decided to post them here.
Why Order Matters
After some further research and testing, I'm still not 100% sure as to the reasoning behind this where types are all the same. Overall, I believe it may be due to the expected types in the cbuffer
and the order of the types in the struct
. In this case, if your cbuffer
expects a bool
first and then a float
, then rearranging causes issues.
cbuffer MaterialBuffer : register(b0) {
bool HasTexture;
float SpecularPower;
float4 Ambient;
...
}
// Won't work.
public struct MaterialBuffer {
public float SpecularPower;
public Vector2 padding2;
public bool HasTexture;
private bool padding0;
private short padding1;
public Color4 Ambient;
...
}
// Works.
public struct MaterialBuffer {
public bool HasTexture;
private bool padding0;
private short padding1;
public float SpecularPower;
public Vector2 padding2;
public Color4 Ambient;
...
}
I put some research effort into testing the differences in byte size of types and this didn't really seem to change much of anything but I'll post my findings for common basic types here:
1 Byte : bool, sbyte, byte
2 Bytes : short, ushort
4 Bytes : int, uint, float
8 Bytes : long, ulong, double
16 Bytes: decimal
You do have to be conscious of the basic types used to construct more complex types. Say for example you have a Vector2
with an X
property and a Y
property. If those are represented by float
types then you'll need an 8 byte padding prior to the next property unless you have some other stuff to help reach 16 bytes. However, if those are represented by double
types or decimal
types then the size is different and you'll need to be aware of that.
Register Assignments
I was able to solve the register issue; this also corresponds to the C#
side when you set the buffers. As you set the buffers, you assign indices to those buffers and the HLSL
is expected to use the same indices.
// Buffer declarations in HLSL.
cbuffer ViewBuffer : register(b0)
cbuffer CameraBuffer : register(b1);
cbuffer MaterialBuffer : register(b2);
// Buffer assignments in C#.
context.VertexShader.SetConstantBuffer(0, viewBuffer);
context.VertexShader.SetConstantBuffer(1, cameraBuffer);
context.VertexShader.SetConstantBuffer(2, materialBuffer);
The above code will work as expected since the buffers are assigned to the correct registers. However, if we change the buffer for the camera to 8 for example, then the cbuffer
would have to be assigned to register b8
in order to work properly. The code below doesn't work for that exact reason.
cbuffer CameraBuffer : register(b1)
context.VertexShader.SetConstantBuffer(8, cameraBuffer);
add a comment |
A Quick Note
I am currently working on further analysis of the problem so that I can give a more detailed and accurate answer. I will update the question and answer to reflect as much accuracy as possible as I discover more details.
Basic Answer
The exact issue with the question above (which was unknown at the time of posting), is that the HLSL
buffers didn't match their C#
representations; thus the reordering of variables caused the shader to fail. However, I am still unsure as to why when the types are the same. I have learned of some other things along my road for an answer and have decided to post them here.
Why Order Matters
After some further research and testing, I'm still not 100% sure as to the reasoning behind this where types are all the same. Overall, I believe it may be due to the expected types in the cbuffer
and the order of the types in the struct
. In this case, if your cbuffer
expects a bool
first and then a float
, then rearranging causes issues.
cbuffer MaterialBuffer : register(b0) {
bool HasTexture;
float SpecularPower;
float4 Ambient;
...
}
// Won't work.
public struct MaterialBuffer {
public float SpecularPower;
public Vector2 padding2;
public bool HasTexture;
private bool padding0;
private short padding1;
public Color4 Ambient;
...
}
// Works.
public struct MaterialBuffer {
public bool HasTexture;
private bool padding0;
private short padding1;
public float SpecularPower;
public Vector2 padding2;
public Color4 Ambient;
...
}
I put some research effort into testing the differences in byte size of types and this didn't really seem to change much of anything but I'll post my findings for common basic types here:
1 Byte : bool, sbyte, byte
2 Bytes : short, ushort
4 Bytes : int, uint, float
8 Bytes : long, ulong, double
16 Bytes: decimal
You do have to be conscious of the basic types used to construct more complex types. Say for example you have a Vector2
with an X
property and a Y
property. If those are represented by float
types then you'll need an 8 byte padding prior to the next property unless you have some other stuff to help reach 16 bytes. However, if those are represented by double
types or decimal
types then the size is different and you'll need to be aware of that.
Register Assignments
I was able to solve the register issue; this also corresponds to the C#
side when you set the buffers. As you set the buffers, you assign indices to those buffers and the HLSL
is expected to use the same indices.
// Buffer declarations in HLSL.
cbuffer ViewBuffer : register(b0)
cbuffer CameraBuffer : register(b1);
cbuffer MaterialBuffer : register(b2);
// Buffer assignments in C#.
context.VertexShader.SetConstantBuffer(0, viewBuffer);
context.VertexShader.SetConstantBuffer(1, cameraBuffer);
context.VertexShader.SetConstantBuffer(2, materialBuffer);
The above code will work as expected since the buffers are assigned to the correct registers. However, if we change the buffer for the camera to 8 for example, then the cbuffer
would have to be assigned to register b8
in order to work properly. The code below doesn't work for that exact reason.
cbuffer CameraBuffer : register(b1)
context.VertexShader.SetConstantBuffer(8, cameraBuffer);
A Quick Note
I am currently working on further analysis of the problem so that I can give a more detailed and accurate answer. I will update the question and answer to reflect as much accuracy as possible as I discover more details.
Basic Answer
The exact issue with the question above (which was unknown at the time of posting), is that the HLSL
buffers didn't match their C#
representations; thus the reordering of variables caused the shader to fail. However, I am still unsure as to why when the types are the same. I have learned of some other things along my road for an answer and have decided to post them here.
Why Order Matters
After some further research and testing, I'm still not 100% sure as to the reasoning behind this where types are all the same. Overall, I believe it may be due to the expected types in the cbuffer
and the order of the types in the struct
. In this case, if your cbuffer
expects a bool
first and then a float
, then rearranging causes issues.
cbuffer MaterialBuffer : register(b0) {
bool HasTexture;
float SpecularPower;
float4 Ambient;
...
}
// Won't work.
public struct MaterialBuffer {
public float SpecularPower;
public Vector2 padding2;
public bool HasTexture;
private bool padding0;
private short padding1;
public Color4 Ambient;
...
}
// Works.
public struct MaterialBuffer {
public bool HasTexture;
private bool padding0;
private short padding1;
public float SpecularPower;
public Vector2 padding2;
public Color4 Ambient;
...
}
I put some research effort into testing the differences in byte size of types and this didn't really seem to change much of anything but I'll post my findings for common basic types here:
1 Byte : bool, sbyte, byte
2 Bytes : short, ushort
4 Bytes : int, uint, float
8 Bytes : long, ulong, double
16 Bytes: decimal
You do have to be conscious of the basic types used to construct more complex types. Say for example you have a Vector2
with an X
property and a Y
property. If those are represented by float
types then you'll need an 8 byte padding prior to the next property unless you have some other stuff to help reach 16 bytes. However, if those are represented by double
types or decimal
types then the size is different and you'll need to be aware of that.
Register Assignments
I was able to solve the register issue; this also corresponds to the C#
side when you set the buffers. As you set the buffers, you assign indices to those buffers and the HLSL
is expected to use the same indices.
// Buffer declarations in HLSL.
cbuffer ViewBuffer : register(b0)
cbuffer CameraBuffer : register(b1);
cbuffer MaterialBuffer : register(b2);
// Buffer assignments in C#.
context.VertexShader.SetConstantBuffer(0, viewBuffer);
context.VertexShader.SetConstantBuffer(1, cameraBuffer);
context.VertexShader.SetConstantBuffer(2, materialBuffer);
The above code will work as expected since the buffers are assigned to the correct registers. However, if we change the buffer for the camera to 8 for example, then the cbuffer
would have to be assigned to register b8
in order to work properly. The code below doesn't work for that exact reason.
cbuffer CameraBuffer : register(b1)
context.VertexShader.SetConstantBuffer(8, cameraBuffer);
edited Nov 19 '18 at 14:18
answered Nov 16 '18 at 21:26
PerpetualJPerpetualJ
863220
863220
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53345125%2fwhy-does-order-matter-in-shaders%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown