Related
Given an array of RBG pixels that updates every frame (e.g. 1024x1024), a ID3D11RenderTargetView, ID3D11Device and ID3D11DeviceContext, what's the easiest way to draw these pixels to the render view?
I've been working the angle of creating a vertex buffer for a square (two triangles), trying to make pixels be a proper texture, and figuring out how to make a shader reference the texture sampler. I've been following this tutorial https://learn.microsoft.com/en-us/windows/uwp/gaming/applying-textures-to-primitives .... But to be honest, I don't see how this tutorial has shaders that even reference the texture data (shaders defined on the proceeding tutorial, here).
I am a total DirectX novice, but I am writing a plugin for an application where I am given a directx11 device/view/context, and need to fill it with my pixel data. Many thanks!
IF you can make sure your staging resource matches the exact resolution and format of the render target you are given:
Create a staging resource
Map the staging resource, and copy your data into it.
Unmap the staging resource
UseGetResource on the RTV to get the resource
CopyResource from your staging to that resource.
Otherwise, IF you can count on Direct3D Hardware Feature level 10.0 or better, the easiest way would be:
Create a texture with USAGE_DYNAMIC.
Map it and copy your data into the texture.
Unmap the resource
Render the dynamic texture as a 'full-screen' quad using the 'big-triangle' self-generation trick in the vertex shader:
SamplerState PointSampler : register(s0);
Texture2D<float4> Texture : register(t0);
struct Interpolators
{
float4 Position : SV_Position;
float2 TexCoord : TEXCOORD0;
};
Interpolators main(uint vI : SV_VertexId)
{
Interpolators output;
// We use the 'big triangle' optimization so you only Draw 3 verticies instead of 4.
float2 texcoord = float2((vI << 1) & 2, vI & 2);
output.TexCoord = texcoord;
output.Position = float4(texcoord.x * 2 - 1, -texcoord.y * 2 + 1, 0, 1);
return output;
}
and a pixel shader of:
float4 main(Interpolators In) : SV_Target0
{
return Texture.Sample(PointSampler, In.TexCoord);
}
Then draw with:
ID3D11ShaderResourceView* textures[1] = { texture };
context->PSSetShaderResources(0, 1, textures);
// You need a sampler object.
context->PSSetSamplers(0, 1, &sampler);
// Depending on your desired result, you may need state objects here
context->OMSetBlendState(nullptr, nullptr, 0xffffffff);
context->OMSetDepthStencilState(nullptr, 0);
context->RSSetState(nullptr);
context->IASetInputLayout(nullptr);
contet->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
Draw(3, 0);
For full source for the "Full Screen Quad" drawing, see GitHub.
I am ramping up OpenGL ES3 in iOS platform and have encountered one trivial issue regarding the FBO's attachment point and presentRenderbuffer call.
Basically, unless the renderbuffer is attached to GL_COLOR_ATTACHMENT0,
"presentRenderbuffer" doesn't work, just blank screen only.
(For instance, if GL_COLOR_ATTACHMENT1 is used, it doesn't work. Only the "0" attachment point works, presenting the correct rendering results on the screen.)
I had added glDrawBuffers to assign the destination buffers but it didn't help.
I have searched many documents and Q&As, but nothing mentioned regarding any restrictions against using non-0 color attachment points in FBO to connect to CAEAGLLayer. Though I could see one diagram in the Apple's OpenGL ES programming guide that shows the "0" attachment point in it as an example diagram but I couldn't see any certain statements. Is it normal? Is only the "0" attachment point allowed? Or what did I miss?
I think it must be a trivial one, but unfortunately it has wasted me a couple of days already, so I came here for help. Thanks, in advance.
GLuint _frameBuffer;
GLuint _colorRenderBuffer;
glGenFramebuffers(1, &_frameBuffer);
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
glFramebufferRenderbuffer( GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT1, // it is NOT "0" attachment point.
GL_RENDERBUFFER,
_colorRenderBuffer);
// for the sake, I had added this, too, but no help.
GLenum drawBufferList[1] = {GL_COLOR_ATTACHMENT1};
glDrawBuffers(1, drawBufferList);
//
// ... rendering to FBO
//
// Somehow this doesn't work
// unless the FBO attachment point is (GL_COLOR_ATTACHMENT0).
BOOL presentSuccess;
presentSuccess = [_context presentRenderbuffer:GL_RENDERBUFFER];
======================================================
Additional posting
that includes the shaders and skeleton of renderer, too.
// -------------------------------------------------
// Vertex Shader
#version 300 es
in vec4 Position; // input position vector
in vec4 SourceColor; // input color
out vec4 DestinationColor; // output color
// uniform
uniform mat4 Projection; // projection matrix
uniform mat4 ModelView; // modelView matrix
in vec2 TexCoordIn; // texture coordinate. Texture image is a 2D data, so.
out vec2 TexCoordOut;
void main(void) {
DestinationColor = SourceColor;
gl_Position = Projection * ModelView * Position;
TexCoordOut = TexCoordIn;
}
// -----------------------------------------
// -----------------------------------------
// Fragment Shader
#version 300 es
in lowp vec4 DestinationColor;
in lowp vec2 TexCoordOut;
uniform sampler2D Texture;
layout(location = 0) out lowp vec4 colorOutput; // "location(0)"
void main(void) {
colorOutput = DestinationColor * texture(Texture, TexCoordOut);
}
// ---------------------------------------------------
// ---------------------------------------------------
// skeleton of renderer
//
- (void)render:(CADisplayLink *)displayLink
{
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
// Even I have tried this,
// to map the location(0) in the fragment shader
// to the GL_COLOR_ATTACHMENT1.
// But it didn't help.
GLenum drawBufferList[1] = { GL_COLOR_ATTACHMENT1 };
glDrawBuffers(1, drawBufferList);
glClearColor(0, 104.0/255, 55.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//
// do rendering...
//
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer[0]);
// draw
// Somehow this doesn't work
// unless the FBO attachment point is (GL_COLOR_ATTACHMENT0).
BOOL presentSuccess;
presentSuccess = [_context presentRenderbuffer:GL_RENDERBUFFER];
}
// ------------------------------
I have updated the post with some results, after having comments from "solidpixel".
Briefly summarizing the results, it is possible to use non-zero attachment points in FBO to attach the renderbuffer whose storage is associated with CAEAGLLayer. But it must be under some constraints that I have mentioned in the code snippet below. Basically, glDrawBuffers doesn't work as it is supposed to be. To be able to use non-zero attachment points for the renderbuffer that has interface to CAEAGLLayer, the fragment shader's output must be set to the "matched" color attachment index. (In another words, (location=0) output in the shader cannot be connected to other than GL_COLOR_ATTACHMENT0. Furthermore, with "this default-like" mapping, glDrawBuffers still needs to be run with a correct mapping array, to be able to use non-0 color attachments in FBO.
I still hope that I would had missed something in the configuration. But I cannot help but saying that setting up of the FBO that has CAEAGLLayer interface looks somewhat different to other "typical" FBOs that don't have CAEAGLLayer interface with them. (For additional example, I have mentioned another mystery in the code snippet, that the FBO with CAEAGLLayer interface doesn't look like allowing to have more than one color attachments. Very weird.)
// **********************************
// The following code snippet shows the case of how to successfully
// setup the FBO and renderbuffer with CAEAGLLayer,
// using non-0 attachment points (GL_COLOR_ATTACHMENTx, x is not '0').
//
// Somehow, glDrawBuffers doesn't work as it is supposed to be.
// To be able to use non-zero attachment color attachment points in FBO,
// three things must be set accordingly to the same color attachment point.
// (I have marked with "<<<<<<<<<<<<<<<<" in the code snippets below.
//
// 1) FBO's attachment point for the renderbuffer that its memory is associated with CAEAGLLayer.
// 2) fragment shader output location index.
// 3) glDrawBuffer's argument array element.
//
// So all three must be set to GL_COLOR_ATTACHMENT1
// to make presentRenderbuffer correctly with the CAEAGLLayer.
// (For glDrawBuffers's input array must be {GL_NONE, GL_COLOR_ATTACHMENT1, GL_NONE}).
//
// This is not what glDrawBuffers is supposed to behave,
// it is supposed to be able to map the fragment shader's output array to
// any preferred color-attachment points.
// I still hope that I would had missed somthing, to have this results,
// however upon this results that I have so far,
// I cannot help but saying the FBO that has interface to CAEAGLLayer is different,
// so it's better to leave it alone, only for the interface to CAEAGLLayer.
//
// ------------------------------------------------
// FBO and renderbuffer setup before rendering.
// This FBO has the renderbuffer
// whose storage is associated with CAEAGLLayer for presentRenderbuffer().
//
GLuint _frameBuffer;
GLuint _colorRenderBuffer;
glGenFramebuffers(1, &_frameBuffer);
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
glFramebufferRenderbuffer( GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT1, // <<<<<<<<<< It is set to the attachment point#1.
GL_RENDERBUFFER,
_colorRenderBuffer);
//
//
// Somehow it is not allowed to have more than one GL_COLOR_ATTACHMENTx renderbuffer attachment
// to "this FBO" (that has the interface to CAEAGLLayer). Otherwise the FBO completeness check fails.
// (Btw, DepthBuffer attachment (GL_DEPTH_ATTACHMENT) is ok.
//
// For instance, if I add the following in this FBO setup, then the FBO completeness test fails.
// I don't know why. But it has no problem with other FBOs that don't have the interface to CAEAGLLayer,
// to have multiple renderbuffer attachments.
// Somehow, the FBO with CAEAGLLayer interface is different to other FBOs that do not have CAEAGLLayer interface.
//
// GLint framebufferWidth;
// GLint framebufferHeight;
// glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth);
// glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight);
//
// GLuint addtionalRenderBuffer;
// glGenRenderbuffers(1, &additionalRenderBuffer);
// glBindRenderbuffer(GL_RENDERBUFFER, additionalRenderBuffer);
// glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, _framebufferWidth, _framebufferHeight);
// glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_RENDERBUFFER, additionalRenderBuffer);
// glBindRenderbuffer(GL_RENDERBUFFER,0);
// -------------------------------------------------
//
// -------------------------------------------------
// Vertex Shader
#version 300 es
in vec4 Position; // input position vector
in vec4 SourceColor; // input color
out vec4 DestinationColor; // output color
// uniform
uniform mat4 Projection; // projection matrix
uniform mat4 ModelView; // modelView matrix
in vec2 TexCoordIn; // texture coordinate. Texture image is a 2D data, so.
out vec2 TexCoordOut;
void main(void) {
DestinationColor = SourceColor;
gl_Position = Projection * ModelView * Position;
TexCoordOut = TexCoordIn;
}
// -----------------------------------------
// -----------------------------------------
// Fragment Shader
#version 300 es
in lowp vec4 DestinationColor;
in lowp vec2 TexCoordOut;
uniform sampler2D Texture;
layout(location = 1) out lowp vec4 colorOutput; // <<<<<<<<<<<<< It must be set to (location = 1).
void main(void) {
colorOutput = DestinationColor * texture(Texture, TexCoordOut);
}
// ---------------------------------------------------
// ---------------------------------------------------
// skeleton of renderer
//
- (void)render:(CADisplayLink *)displayLink
{
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
// >>>>>>>>>>>> The glDrawBuffer must mention attachment point#1 correctly.
GLenum drawBufferList[3] = { GL_NONE, GL_COLOR_ATTACHMENT1, GL_NONE };
glDrawBuffers(3, drawBufferList); // >>>>>>>>>>>> The size of buffer "3" must be entered correctly.
glClearColor(0, 104.0/255, 55.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//
// do rendering...
//
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
// draw
BOOL presentSuccess;
presentSuccess = [_context presentRenderbuffer:GL_RENDERBUFFER];
}
// ------------------------------
Additional update regarding glDrawBuffers.
I had carefully reviewed the glDrawBuffers in the OpenGL ES 3.0 specification and had found the following statements:
void glDrawBuffers( GLsizei n, const GLenum *bufs);
"GL_INVALID_OPERATION is generated if the GL is bound to a framebuffer object and the ith buffer listed in bufs is anything other than GL_NONE or GL_COLOR_ATTACHMENTSi."
This means, the i-th buffer in the buffer array for the input argument of glDrawBuffers must be either GL_COLOR_ATTACHMENT(i) or GL_NONE.
(i = 0, 1,2,..., GL_MAX_DRAW_BUFFERS)
For example, the 2nd buffer in the buffer array must be
either "GL_COLOR_ATTACHMENT1" or "GL_NONE".
Any GL_COLOR_ATTACHMENTx other than (x=1), causes the error code: GL_INVALID_OPERATION.
If GL_NONE is disregarded (for example), then there is only one buffer array can exist:
{ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, ... }
In that way, the functionality of glDrawBuffers is nothing but an on/off switch between the user defined output variables of fragment shader and FBO. Basically, it is not possible to map the fragment shader's output to FBO's color attachment points in any desired ways, because the mapping is already fixed. location-0 => color attachment pt0, location-1 => pt1, etc. The only way to change the color attachment point for the shader's output is to change the location of the output in the shader itself. If it is true, I don't understand what the reason for this function glDrawBuffers. Anyway, if the buffer array is built in this way, with its index as increasing order (0,1,2,3,...) for the color attachment points, the code works. But this is very disappointing and also makes me very confused because it is against the descriptions of glDrawBuffers function in several other books and articles as well. However, no matter what others have mentioned, only the standard specification matters, so I cannot help but accepting this as a fact.
So returning to the original question how to use non-zero attachment point in FBO for presentRenderbuffer function can be implemented as follows. In this case, GL_COLOR_ATTACHMENT1 is used.
//
// fragment shader's output
layout(location = 1) out lowp vec4 colorOutput;
//
// FBO and renderbuffer setup.
GLuint _frameBuffer;
GLuint _colorRenderBuffer;
glGenFramebuffers(1, &_frameBuffer);
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
glFramebufferRenderbuffer( GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT1, // non-zero color att-pt.
GL_RENDERBUFFER,
_colorRenderBuffer);
//
// in the renderer, the contents of that renderbuffer is presented.
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
// the attachment pt must be GL_COLOR_ATTACHMENT1,
// because the shader output location is "1".
GLenum drawBufferList[3] = { GL_NONE, GL_COLOR_ATTACHMENT1, GL_NONE };
glDrawBuffers(3, drawBufferList);
//
// do rendering ,,,,
//
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
BOOL presentSuccess;
presentSuccess = [_context presentRenderbuffer:GL_RENDERBUFFER];
Do a good deed and help get someone (me) out of their misery, since it's New Year's Eve soon. I'm working on an iOS app, a coloring book for kids and I haven't stumbled upon OpenGL before (more precisely OpenGLES 2.0) so there's a big chance there's stuff I don't actually get in my code.
One of the tasks is to not let the brush spill out of the contour in which the user started drawing.
After reading and understanding some OpenGL basics, I found that using the stencil buffer is the right solution. This is my stencil buffer setup:
glClearStencil(0);
//clear the stencil
glClear(GL_STENCIL_BUFFER_BIT);
//disable writing to color buffer
glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
//disable depth buffer
glDisable(GL_DEPTH_TEST);
//enable writing to stencil buffer
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NEVER, 1, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
[self drawStencil];
//re-enable color buffer
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
//only draw where there is a 1
glStencilFunc(GL_EQUAL, 1, 1);
//keep the pixels in the stencil buffer
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
Right now, I'm just trying to draw a square in the stencil buffer and see if I can limit my drawing only to that square. This is the method drawing the square:
- (void)drawStencil
{
// Create a renderbuffer
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
// Create a framebuffer
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);
// Clear
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
// Read vertex shader source
NSString *vertexShaderSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"VertexShader" ofType:#"vsh"] encoding:NSUTF8StringEncoding error:nil];
const char *vertexShaderSourceCString = [vertexShaderSource cStringUsingEncoding:NSUTF8StringEncoding];
// Create and compile vertex shader
GLuint _vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(_vertexShader, 1, &vertexShaderSourceCString, NULL);
glCompileShader(_vertexShader);
// Read fragment shader source
NSString *fragmentShaderSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"FragmentShader" ofType:#"fsh"] encoding:NSUTF8StringEncoding error:nil];
const char *fragmentShaderSourceCString = [fragmentShaderSource cStringUsingEncoding:NSUTF8StringEncoding];
// Create and compile fragment shader
GLuint _fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(_fragmentShader, 1, &fragmentShaderSourceCString, NULL);
glCompileShader(_fragmentShader);
// Create and link program
GLuint program = glCreateProgram();
glAttachShader(program, _vertexShader);
glAttachShader(program, _fragmentShader);
glLinkProgram(program);
// Use program
glUseProgram(program);
// Define geometry
GLfloat square[] = {
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
0.5, 0.5};
//Send geometry to vertex shader
const char *aPositionCString = [#"a_position" cStringUsingEncoding:NSUTF8StringEncoding];
GLuint aPosition = glGetAttribLocation(program, aPositionCString);
glVertexAttribPointer(aPosition, 2, GL_FLOAT, GL_FALSE, 0, square);
glEnableVertexAttribArray(aPosition);
// Draw
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Present renderbuffer
[context presentRenderbuffer:GL_RENDERBUFFER];
}
So much code and nothing happens... I can draw relentlessly wherever I want without a single stencil test stopping me.
What can I do? How do I check if the stencil buffer has something drawn inside it? If there's a missing puzzle for any of you, I will happily share any other parts of the code.
Any help is greatly appreciated! This has been torturing me for a while now. I will be forever in your debt!
UPDATE
I got the contour thing to work but I didn't use the stencil buffer. I created masks for every drawing area and textures for each mask which I loaded in the fragment shader along with the brush texture. When I tap on an area, I iterate through the array of masks and see which one was selected and bind the mask texture. I will make another post on SO with a more appropriate title and explain it there.
The way you allocate the renderbuffer storage looks problematic:
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
The documentation says about this method:
The width, height, and internal color buffer format are derived from the characteristics of the drawable object.
The way I understand it, since your "drawable object" will normally be a color buffer, this will create a color renderbuffer. But you need a renderbuffer with stencil format in your case. I'm not sure if there's a way to do this with a utility method in the context class (the documentation says something about "overriding the internal color buffer format"), but the easiest way is probably to simply call the corresponding OpenGL function directly:
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
If you want to use your own FBO for this rendering, you will also need to create a color buffer for it, and attach it to the FBO. Otherwise you're not really producing any rendering output.
Instead of creating a new FBO, it might be easier to make sure that the default framebuffer has a stencil buffer, and render to it directly. To do this, you can request a stencil buffer for your GLKView derived view by making this call during setup:
[view setDrawableStencilFormat: GLKViewDrawableStencilFormat8];
I'm writing an app on iOS allows drawing free style (using finger) and drawing image on screen. I use OpenGL ES to implement. I have 2 functions, one is drawing free style, one is drawing texture
--- Code drawing free style
- (void)drawFreeStyle:(NSMutableArray *)pointArray {
//Prepare vertex data
.....
// Load data to the Vertex Buffer Object
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, vertexCount*2*sizeof(GLfloat), vertexBuffer, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, 0);
**GLuint a_ver_flag_drawing_type = glGetAttribLocation(program[PROGRAM_POINT].id, "a_drawingType");
glVertexAttrib1f(a_ver_flag_drawing_type, 0.0f);
GLuint u_fra_flag_drawing_type = glGetUniformLocation(program[PROGRAM_POINT].id, "v_drawing_type");
glUniform1f(u_fra_flag_drawing_type, 0.0);**
glUseProgram(program[PROGRAM_POINT].id);
glDrawArrays(GL_POINTS, 0, (int)vertexCount);
// Display the buffer
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];
}
--- Code drawing texture
- (void)drawTexture:(UIImage *)image atRect:(CGRect)rect {
GLuint a_ver_flag_drawing_type = glGetAttribLocation(program[PROGRAM_POINT].id, "a_drawingType");
GLuint u_fra_flag_drawing_type = glGetUniformLocation(program[PROGRAM_POINT].id, "v_drawing_type");
GLuint a_position_location = glGetAttribLocation(program[PROGRAM_POINT].id, "a_Position");
GLuint a_texture_coordinates_location = glGetAttribLocation(program[PROGRAM_POINT].id, "a_TextureCoordinates");
GLuint u_texture_unit_location = glGetUniformLocation(program[PROGRAM_POINT].id, "u_TextureUnit");
glUseProgram(PROGRAM_POINT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texName);
glUniform1i(u_texture_unit_location, 0);
glUniform1f(u_fra_flag_drawing_type, 1.0);
const float textrect[] = {-1.0f, -1.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f};
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(textrect), textrect, GL_STATIC_DRAW);
glVertexAttrib1f(a_ver_flag_drawing_type, 1.0f);
glVertexAttribPointer(a_position_location, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(0));
glVertexAttribPointer(a_texture_coordinates_location, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glEnableVertexAttribArray(a_ver_flag_drawing_type);
glEnableVertexAttribArray(a_position_location);
glEnableVertexAttribArray(a_texture_coordinates_location);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];
}
Notice 2 variables a_ver_flag_drawing_type (attribute) and u_fra_flag_drawing_type (uniform). They're use for setting flags on vertex shader and fragment shader to determine drawing free style or texture on both files
--- Vertex shader
//Flag
attribute lowp float a_drawingType;
//For drawing
attribute vec4 inVertex;
uniform mat4 MVP;
uniform float pointSize;
uniform lowp vec4 vertexColor;
varying lowp vec4 color;
//For texture
attribute vec4 a_Position;
attribute vec2 a_TextureCoordinates;
varying vec2 v_TextureCoordinates;
void main()
{
if (abs(a_drawingType - 1.0) < 0.0001) {
//Draw texture
v_TextureCoordinates = a_TextureCoordinates;
gl_Position = a_Position;
} else {
//Draw free style
gl_Position = MVP * inVertex;
gl_PointSize = pointSize;
color = vertexColor;
}
}
--- Fragment shader
precision mediump float;
uniform sampler2D texture;
varying lowp vec4 color;
uniform sampler2D u_TextureUnit;
varying vec2 v_TextureCoordinates;
uniform lowp float v_drawing_type;
void main()
{
if (abs(v_drawing_type - 1.0) < 0.0001) {
//Draw texture
gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);
} else {
//Drawing free style
gl_FragColor = color * texture2D(texture, gl_PointCoord);
}
}
My idea is setting these flags from drawing code at drawing time. Attribute a_drawingType is used for vertex shader and Uniform v_drawing_type is used for fragment shader. Depending these flags to know draw free style or texture.
But if I run independently, each time just one type (if run drawing free style, comment code config drawing texture on vertex shader and fragment shader file and vice versa) it can draw as I want. If I combine them, it not only can't draw but also makes app crash as
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
I'm new for OpenGL ES and GLSL language, so I'm not sure my thinking about setting flags like that is right or wrong. Can anyone help me
So why don't you just build 2 seperate shader programs and useProgram() on one of them, instead of sending flag values to GL and making expensive conditional branch in vertex and especially fragment shader?
Attributes are per-vertex, uniforms are per-shader program.
You might see a crash if you supplied only one value for an attribute then asked OpenGL to draw, say, 100 points. In that case OpenGL is going to do an out-of-bounds array access when it attempts to fetch the attributes for vertices 2–100.
It'd be more normal to use two separate programs. Conditionals are very expensive on GPUs because GPUs try to maintain one program counter while processing multiple fragments. They're SIMD units. Any time the evaluation of an if differs between two neighbouring fragments you're probably reducing parallelism. The idiom is therefore not to use if statements where possible.
If you switch to a uniform there's a good chance you won't lose any performance through absent parallelism because the paths will never diverge. Your GLSL compiler may even be smart enough to recompile the shader every time you reset the constant, effectively performing constant folding. But you'll be paying for the recompilation every time.
If you just had two programs and switched between them you wouldn't pay the recompilation fee.
It's not completely relevant in this case but e.g. you'd also often see code like:
if (abs(v_drawing_type - 1.0) < 0.0001) {
//Draw texture
gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);
} else {
//Drawing free style
gl_FragColor = color * texture2D(texture, gl_PointCoord);
}
Written more like:
gl_FragColor = mix(texture2D(u_TextureUnit, v_TextureCoordinates),
color * texture2D(texture, gl_PointCoord),
v_drawing_type);
... because that avoids the conditional entirely. In this case you'd want to adjust the texture2D so that both calls were identical, and probably factor them out of the call, to ensure you don't end up always doing two samples instead of one.
The previously posted answers explain certain pieces, but an important part is missing in all of them. It is perfectly legal to specify a single attribute value that is applied to all vertices in a draw call. What you did here was basically valid:
glVertexAttrib1f(a_ver_flag_drawing_type, 1.0f);
The direct problem was this call that followed shortly after:
glEnableVertexAttribArray(a_ver_flag_drawing_type);
There are two main ways to specify the value of a vertex attribute:
Use the current value, as specified in your case by glVertexAttrib1f().
Use values from an array, as specified with glVertexAttribPointer().
You select which of the two options is used for any given attribute by enabling/disabling the array, which is done by calling glEnableVertexAttribArray()/glDisableVertexAttribArray().
In the posted code, the vertex attribute was specified as only a current value, but the attribute was then enabled to fetch from an array with glEnableVertexAttribArray(). This conflict caused the crash, because the attribute values would have been fetched from an array that was never specified. To use the specified current value, the call simply has to be changed to:
glDisableVertexAttribArray(a_ver_flag_drawing_type);
Or, if the array was never enabled, the call could be left out completely. But just in case another part of the code might have enabled it, it's safer to disable it explicitly.
As a side note, the following statement sequence from the first draw function also looks suspicious. glUniform*() sets value on the active program, so this will set a value on the previously active program, not the one specified in the second statement. If you want to set the value on the new program, the order of the statements has to be reversed.
glUniform1f(u_fra_flag_drawing_type, 0.0);
glUseProgram(program[PROGRAM_POINT].id);
On the whole thing, I think there are at least two approaches that are better than the one chosen:
Use separate shader programs for the two different types of rendering. While using a single program with switchable behavior is a valid option, it looks artificial, and using separate programs seems much cleaner.
If you want to stick with a single program, use a single uniform to do the switching, instead of using an attribute and a uniform. You could use the one you already have, but you might just as well make it a boolean while you're at it. So in both the vertex and fragment shader, use the same uniform declaration:
uniform bool v_use_texture;
Then the tests become:
if (v_use_texture) {
Getting the uniform location is the same as before, and you can set the value, which will the be available in both the vertex and fragment shader, with one of:
glUniform1i(loc, 0);
glUniform1i(loc, 1);
I found the problem, just change variable a_drawingType from Attribute to Uniform then use glGetUniformLocation and glUniform1f to get index and pass value. I think Attribute will pass for any vertex so use uniform to pass once.
I want to apply a pixel shader onto my background sprite, to create some sort of lighting.
So i draw a Render Target with the light on it and want to merge it onto the background via the Pixel shader.
This is the essential code:
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
lighting.Parameters["lightMask"].SetValue(lightingMask);
lighting.CurrentTechnique.Passes[0].Apply();
spriteBatch.Draw(hexBack, new Vector2(0, 0), Color.White);
spriteBatch.End();
In this case, hexBack is the Rendertarget with a simple sprite drawn in it and lightingMask is the rendertarget with the light texture in it.
Both are Backbuffer width and height.
So when i try to run the program, it crashes with:
XNA Framework Reach profile requires TextureAddressMode to be Clamp when using texture sizes that are not powers of two.
So i tried to set up clamping, but i cant find a way to get it working.
The shader code:
texture lightMask;
sampler mainSampler : register(s0);
sampler lightSampler = sampler_state{Texture = lightMask;};
struct PixelShaderInput
{
float4 TextureCoords: TEXCOORD0;
};
float4 PixelShaderFunction(PixelShaderInput input) : COLOR0
{
float2 texCoord = input.TextureCoords;
float4 mainColor = tex2D(mainSampler, texCoord);
float4 lightColor = tex2D(lightSampler, texCoord);
return mainColor * lightColor;
}
technique Technique1
{
pass Pass1
{
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}
Thanks for your help!
pcnx
If you are unable to use power of two textures, you have to change your Spritebath.begin call and specify a SamplerState. The minimum to specify should be
public void Begin (
SpriteSortMode sortMode,
BlendState blendState,
SamplerState samplerState,
DepthStencilState depthStencilState,
RasterizerState rasterizerState
)
The error refers to your texture addressing mode (ie: does the texture wrap around at the edges, or is it clamped at the edges). Nothing to do with a shader.
Use one of the overloads for SpriteBatch.Begin (MSDN) that takes a SamplerState, and pass in SamplerState.LinearClamp (MSDN).
The default for SpriteBatch.Begin is SamplerState.LinearClamp, so you must be setting a different state (eg: LinearWrap) onto the graphics device somewhere else in your code? Don't do that.
(Alternately: change from the Reach profile to the HiDef profile in your project settings.)