I'm running a C# project with the Pencil.Gaming library, which is the C# binding of GLFW which uses OpenTK to wrap OpenGL functions. I am trying to draw an array of Vertices in a vbo, and once I get to GL.DrawArrays, it gives me a System.AccessViolationException and an Invalid Operation error when I call GL.GetError(). Below is my code for the Mesh class and a screencap of the error is Visual Studio. The object I am trying to draw and a copy of my Vertex class is also below.
static Mesh mesh;
mesh = new Mesh();
Vertex[] data = new Vertex[] {new Vertex(new Vector3(-1,-1,0)),
new Vertex(new Vector3(0,1,0)),
new Vertex(new Vector3(1,-1,0))};
mesh.addVertices(data);
mesh.draw();
The Vertex Class:
struct Vertex
{
public static readonly int SIZE = 3;
private Vector3 position;
public Vertex(Vector3 position)
{
this.position = position;
}
}
The Mesh class:
using System;
using Pencil.Gaming.Graphics;
using Pencil.Gaming.MathUtils;
namespace Practicalis.Rendering
{
class Mesh
{
private int size;
private int vbo;
public Mesh()
{
size = 0;
GL.GenBuffers(size, out vbo);
}
public void addVertices(Vertex[] vertices)
{
size = vertices.Length * sizeof(float);
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)size, (IntPtr)vbo, BufferUsageHint.StaticDraw);
}
public void draw()
{
GL.EnableVertexAttribArray(0);
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, Vertex.SIZE * 4, 0);
Console.WriteLine(GL.GetError());
GL.DrawArrays(BeginMode.Triangles, 0, size);
GL.DisableVertexAttribArray(0);
}
}
}
GL.GenBuffers(size, out vbo);
Before this line you have size 0. It should be at least one since thats the number of buffers to be generated.
In the add vertice function, I wasn't passing in my actual Vertex array, just the pointer to vbo. The proper function is below.
public void addVertices(Vertex[] vertices)
{
size = vertices.Length * Vector3.SizeInBytes;
Vector3[] vertsData = new Vector3[vertices.Length];
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
for (int i = 0; i < vertices.Length; i++)
vertsData[i] = vertices[i].Position;
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)size, vertsData, BufferUsageHint.StaticDraw);
}
Related
I'm learning OpenGL with the openTK librairy in c#.
I've managed to successefuly renderer triangles on my window,
and things are running properly.
However, the performance is terrible (running at half a fps with only 100k triangles).
My question is, is there anything that I am doing badly ?
how could I improve performances ?
for the most part, I've followed this tutorial translated from c++.
Here is the render method, called each frame :
public static void Render()
{
//render the mesh render queue to the screen and clear it
GL.UseProgram(shaderProgram.id);
//meshRenderQueue is a List<Mesh>
foreach(Mesh mesh in meshRenderQueue)
{
mesh.Draw();
}
meshRenderQueue.Clear();
}
Here is my Mesh class :
public class Mesh
{
private Vertex[] vertices;
private uint[] triangles;
private int VAO;
private int VBO;
private int EBO;
public Mesh()
{
//create lots of unoptimized triangles to test performances
int triCount = 100000;
vertices = new Vertex[3 * triCount];
for(int i = 0; i < triCount; i++)
{
vertices[3 * i + 0] = new Vertex(new Vector3(-0.5f, -0.5f, 0f));
vertices[3 * i + 1] = new Vertex(new Vector3(0.5f, -0.5f, 0f));
vertices[3 * i + 2] = new Vertex(new Vector3(0, 0.5f, 0f));
};
triangles = new uint[3 * triCount];
for (int i = 0; i < triCount; i++)
{
triangles[3 * i + 0] = 0;
triangles[3 * i + 1] = 1;
triangles[3 * i + 2] = 2;
};
SetUpMesh();
}
private void SetUpMesh()
{
//initialize the openGL buffers
VAO = GL.GenVertexArray();
VBO = GL.GenBuffer();
EBO = GL.GenBuffer();
GL.BindVertexArray(VAO);
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * 8 * 4, vertices, BufferUsageHint.StaticDraw);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, EBO);
GL.BufferData(BufferTarget.ElementArrayBuffer, triangles.Length * sizeof(int), triangles, BufferUsageHint.StaticDraw);
//vertex positions
GL.EnableVertexAttribArray(0);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 8 * 4, 0);
//vertex normals
GL.EnableVertexAttribArray(1);
GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 8 * 4, 3 * 4);
//vertex texture coords
GL.EnableVertexAttribArray(2);
GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, 8 * 4, 6 * 4);
//unbind vertex array (what for ?)
GL.BindVertexArray(0);
}
public void Draw()
{
//render the mesh with the given shader
GL.BindVertexArray(VAO);
GL.DrawElements(BeginMode.Triangles, triangles.Length, DrawElementsType.UnsignedInt, 0);
GL.BindVertexArray(0);
}
}
And finally the Vertex struct, if usefull :
public struct Vertex
{
public Vector3 position;
public Vector3 normal;
public Vector2 textureCoords;
public Vertex(Vector3 pos, Vector3 norm, Vector2 textCoords)
{
position = pos;
normal = norm;
textureCoords = textCoords;
}
public Vertex(Vector3 pos)
{
position = pos;
normal = Vector3.zero;
textureCoords = new Vector2(0, 0);
}
}
Thanks in advance for any help. cheers !
I am trying to draw a triangle shape in Android Xamarin with C#. The triangle is a class with the method draw. In this draw method, there is an openGL20 method for coloring the triangle object we just created. Whenever the execution in the draw method reaches this color object method, this exception is thrown Java.Lang.IllegalArgumentException Message=length - offset < count*4 < needed.
I really can't understand what that error means but here is the code am using so far
public class MyGLRenderer : Java.Lang.Object, GLSurfaceView.IRenderer
{
//Renderer method to draw the triangle object
public void OnDrawFrame(IGL10 gl)
{
GLES20.GlClear(GLES20.GlColorBufferBit);
Triangle triangle = new Triangle();
triangle.draw();
}
//Method to set the view and the height of the painting window
public void OnSurfaceChanged(IGL10 gl, int width, int height)
{
GLES20.GlViewport(0, 0, width, height);
}
public void OnSurfaceCreated(IGL10 gl, Javax.Microedition.Khronos.Egl.EGLConfig config)
{
//Set the background frame color
GLES20.GlClearColor(0.0f, 0.0f, 1.0f, 0.0f);
GLES20.GlDrawArrays(GLES20.GlColorBufferBit, 2, 10);
}
}
Code below defines the Triangle class and the OPENGL20 color method thats throwing an exception commented upon
public class Triangle
{
private FloatBuffer vertexBuffer;
//Number of co-ordinates per vertex in this context
private static readonly int Coords_per_vert = 3;
private static float[] triangleCoords = new float[] {
0.0f,0.622008459f,0.0f, //top
-0.5f,-0.311004243f,0f , //bottom left
0.5f, -0.311004243f,0.0f //bottom right
};
//Set color with red, green, blue and alpha values
private float[] color = new float[] { 0.63671875f, 0.76953125f, 0.22265625f };
private readonly int mProgram;
public Triangle()
{
//Initialize vertex byte buffer for shape co-ordinates
ByteBuffer bb = ByteBuffer.AllocateDirect(triangleCoords.Length * 4);
//Use the device native byte order
bb.Order(ByteOrder.NativeOrder());
FloatBuffer myfloat = bb.AsFloatBuffer();
//Create floating point buffer from ByteBuffer
vertexBuffer = bb.AsFloatBuffer();
vertexBuffer.Put(triangleCoords);
vertexBuffer.Position(0);
int vertexShader = MyGLRenderer.loadShader(GLES20.GlVertexShader, vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GlFragmentShader, fragmentShaderCode);
//Create empty opengles program
mProgram = GLES20.GlCreateProgram();
//Add vertex shader to program
GLES20.GlAttachShader(mProgram, vertexShader);
//Add Fragment to shader
GLES20.GlAttachShader(mProgram, fragmentShader);
//Create openGL program executables
GLES20.GlLinkProgram(mProgram);
}
private readonly string vertexShaderCode = "attribute vec4 vPosition;" +
"void main(){" +
" gl_Position=vPosition;" +
"}";
private readonly string fragmentShaderCode = "precision mediump float;" +
"uniform vec4 vColor;" +
"void main(){" +
"gl_FragColor=vColor;" +
"}";
private int positionHandle, colorHandle;
private readonly int vertexCount = triangleCoords.Length / Coords_per_vert;
private readonly int vertexStride = Coords_per_vert * 4;
//Create a method for drawing the triangle
public void draw()
{
//Add Program to open GLES Environment
GLES20.GlUseProgram(mProgram);
//Get handle to vertex shader's vPosition member
positionHandle = GLES20.GlGetAttribLocation(mProgram, "vPosition");
//Enable a handle to the triangle's vertices
GLES20.GlEnableVertexAttribArray(positionHandle);
//Prepare the triangle co ordinate data
GLES20.GlVertexAttribPointer(positionHandle, Coords_per_vert, GLES20.GlFloat, false, vertexStride, vertexBuffer);
//Get handle to fragment shader's vColor Member
colorHandle = GLES20.GlGetUniformLocation(mProgram, "vColor");
//Set color for drawing the triangle
GLES20.GlUniform4fv(colorHandle, 1, color, 3);
//Draw the triangle, this method causes an exception
GLES20.GlDrawArrays(GLES20.GlTriangles, 0, vertexCount);
//Disable vertex array
GLES20.GlDisableVertexAttribArray(positionHandle);
}
}
Please help me color the triangle object with success, what am i doing wrong
You are using a 3 sized float array for a vec4 uniform.
Add the alpha array to your color array like this
Replace
private float[] color = new float[] { 0.63671875f, 0.76953125f, 0.22265625f };
with
private float[] color = new float[] { 0.63671875f, 0.76953125f, 0.22265625f,1.0f};
with the last member of the second array representing the alpha or opacity of the color being drawn
I'm trying to learn about compute shaders, and this project is really awesome. It is https://github.com/TheAllenChou/unity-cj-lib
But when I run it, there are errors I can't figure out how to get rid of:
ArgumentNullException: Value cannot be null. Parameter name: shader
ArgumentNullException: Value cannot be null. Parameter name: material
Compute shader (ParticleLogic): Property (particleBuffer) at kernel index (1) is not set
Any idea on how to fix? I'm using Unity 2019.3.0a7
It's only happening for the code for the "Turbulent Rainbow GPU Particles" example, so I've been going to the files and changing filepaths, and trying to feed it a material manually.
I just don't think I understand what's happening enough to ever fix it on my own, so I am reaching out for assistance. Appreciate any help.
Relevant code...
using UnityEngine;
using CjLib;
namespace TurbulentRainbowGpuParticles
{
public class Main : MonoBehaviour
{
public ComputeShader m_shader;
private const int kNumParticles = 10000;
/*
private struct Particle
{
// 4 floats
Vector3 m_position;
float m_damping;
// 4 floats
Quaternion m_rotation;
// 4 floats
Vector3 m_linearVelocity;
float m_scale;
// 4 floats
Quaternion m_angularVelocity;
// 4 floats
Vector4 m_lifetime;
// 4 floats
Color m_color;
};
*/
private ComputeBuffer m_computeBuffer;
private ComputeBuffer m_instanceArgsBuffer;
//private Particle[] m_debugBuffer;
private Mesh m_mesh;
private Material m_material;
private MaterialPropertyBlock m_materialProperties;
private int m_csInitKernelId;
private int m_csStepKernelId;
private int m_csParticleBufferId;
private int m_csScaleId;
private int m_csDampingId;
private int m_csSpeedId;
private int m_csLifetimeId;
private int m_csNumParticlesId;
private int m_csTimeId;
void OnEnable()
{
m_mesh = new Mesh();
m_mesh = PrimitiveMeshFactory.BoxFlatShaded();
int particleStride = sizeof(float) * 24;
m_computeBuffer = new ComputeBuffer(kNumParticles, particleStride);
uint[] instanceArgs = new uint[] { 0, 0, 0, 0, 0 };
m_instanceArgsBuffer = new ComputeBuffer(1, instanceArgs.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
instanceArgs[0] = (uint) m_mesh.GetIndexCount(0);
instanceArgs[1] = (uint) kNumParticles;
instanceArgs[2] = (uint) m_mesh.GetIndexStart(0);
instanceArgs[3] = (uint) m_mesh.GetBaseVertex(0);
m_instanceArgsBuffer.SetData(instanceArgs);
//m_debugBuffer = new Particle[kNumParticles];
m_csInitKernelId = m_shader.FindKernel("Init");
m_csStepKernelId = m_shader.FindKernel("Step");
m_csParticleBufferId = Shader.PropertyToID("particleBuffer");
m_csScaleId = Shader.PropertyToID("scale");
m_csDampingId = Shader.PropertyToID("damping");
m_csSpeedId = Shader.PropertyToID("speed");
m_csLifetimeId = Shader.PropertyToID("lifetime");
m_csNumParticlesId = Shader.PropertyToID("numParticles");
m_csTimeId = Shader.PropertyToID("time");
m_material = new Material(Shader.Find("CjLib/Example/TurbulentRainbowGpuParticles"));
m_material.enableInstancing = true;
m_material.SetBuffer(m_csParticleBufferId, m_computeBuffer);
m_materialProperties = new MaterialPropertyBlock();
m_shader.SetFloats(m_csScaleId, new float[] { 0.15f, 0.3f });
m_shader.SetFloat(m_csDampingId, 6.0f);
m_shader.SetFloats(m_csSpeedId, new float[] { 3.0f, 4.0f, 1.0f, 6.0f });
m_shader.SetFloats(m_csLifetimeId, new float[] { 0.1f, 0.5f, 0.5f, 0.1f });
m_shader.SetInt(m_csNumParticlesId, kNumParticles);
m_shader.SetBuffer(m_csInitKernelId, m_csParticleBufferId, m_computeBuffer);
m_shader.SetBuffer(m_csStepKernelId, m_csParticleBufferId, m_computeBuffer);
m_shader.Dispatch(m_csInitKernelId, kNumParticles, 1, 1);
//m_computeBuffer.GetData(m_debugBuffer);
}
void Update()
{
m_shader.SetFloats(m_csTimeId, new float[] { Time.time, Time.fixedDeltaTime });
m_shader.Dispatch(m_csStepKernelId, kNumParticles, 1, 1);
//m_computeBuffer.GetData(m_debugBuffer);
Graphics.DrawMeshInstancedIndirect(m_mesh, 0, m_material, new Bounds(Vector3.zero, 20.0f * Vector3.one), m_instanceArgsBuffer, 0, m_materialProperties, UnityEngine.Rendering.ShadowCastingMode.On);
}
void OnDisable()
{
if (m_computeBuffer != null)
{
m_computeBuffer.Dispose();
m_computeBuffer = null;
}
if (m_instanceArgsBuffer != null)
{
m_instanceArgsBuffer.Dispose();
m_instanceArgsBuffer = null;
}
}
}
}
Fix:
Switch to the latest stable Unity version,
Fix Shader.Find via this method (unity Shader returns a NULL when using Shader.Find)
(optional) If you get C# language specification error like I did, visit TimeWalk-Org's post https://forum.unity.com/threads/feature-xxx-cannot-be-used-because-it-is-not-part-of-the-c-4-0-language-specification.575008/
I probably do something really stupid so go ahead and correct me.
I'm trying to make vertex move so...
In OnLoad I'm setting the VBO like you see.
In OnFrameRender I'm trying to edit vertex then i do drawArrays.
But i can't get this changes be visible in OpenTk window. Doing ConsoleWrite(prymi.vertOne) shows me that struct is edited (i do get sin from 0 to 20) so I'm assuming that this values are not updated on GPU. I tryed every VBO BufferUsageHint (Dynamic/Static/Stream(Copy/Draw/Read)) and this is not the case. I have read that i should use BufferSubData (i don't know about this concept yet(so any help would be helpful) but editing the object which is passed as reference with VBO StreamDraw should also do it... or em i wrong?
Thanks all :)
struct TrianglePrymi
{
public Vector2 vertOne;
public Vector2 vertSec;
public Vector2 VertThird;
public TrianglePrymi(Vector2 fi, Vector2 sec, Vector2 thi )
{
vertOne = fi;
vertSec = sec;
VertThird = thi;
}
public void edit(Vector2 a)
{
vertOne = a;
}
public static int retSize()
{
return (3 * Vector2.SizeInBytes);
}
}
protected override void OnLoad(EventArgs e)
{
prymi = new TrianglePrymi(new Vector2(20,20),new Vector2(300,200), new Vector2(530,420));
ttttt = TrianglePrymi.retSize();
VBO = GL.GenBuffer();
GL.BindVertexArray(VAO);
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)ttttt,ref prymi, BufferUsageHint.DynamicDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
base.OnLoad(e);
}
protected override void OnRenderFrame(FrameEventArgs e)
{
Console.WriteLine(prymi.vertOne[0]);
//Console.WriteLine(prymi.vertSec);
//Console.WriteLine(prymi.VertThird);
magicNumber += 1.00f;
float x = Math.Abs(20 * (float)Math.Sin(magicNumber));
float y = Math.Abs(20 * (float)Math.Cos(magicNumber));
GL.EnableClientState(ArrayCap.VertexArray);
GL.Clear(ClearBufferMask.ColorBufferBit|ClearBufferMask.DepthBufferBit);
GL.ClearColor(Color.Gray);
prymi.edit(new Vector2(x, y));
Vector2 vec = new Vector2(magicNumber, 450);
Matrix4 world = Matrix4.CreateOrthographicOffCenter(0, widX, 0, heiY, 0, 2000);
GL.LoadMatrix(ref world);
GL.Color3(Color.Yellow);
prymi.vertOne = vec;
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
GL.VertexPointer(2, VertexPointerType.Float,Vector2.SizeInBytes, 0);
GL.DrawArrays(BeginMode.Triangles, 0, 3);
GL.End();
GL.Flush();
base.OnRenderFrame(e);
this.SwapBuffers();
}
Short Version of Problem
I am trying to access the contents of a RenderTexture in Unity which I have been drawing with an own Material using Graphics.Blit.
Graphics.Blit (null, renderTexture, material);
My material converts some yuv image to rgb successfully, which I have tested by assigning it to the texture of an UI element. The result is the correct RGB image visible on the screen.
However, I also need the raw data for a QR code scanner. I am doing this like I would access it from a camera as explained here. In a comment down there, it was mentioned that the extraction is also possible from a RenderTexture that was filled with Graphics.Blit. But when I am trying to that, my texture only contains the value 205 everywhere. This is the code I am using in the Update function, directly after the Graphics.Blit call:
RenderTexture.active = renderTexture;
texture.ReadPixels (new Rect (0, 0, width, height), 0, 0);
texture.Apply ();
RenderTexture.active = null;
When assigning this texture to the same UI element, it is gray and slightly transparent. When viewing the image values, they are all 205.
Why is the possible? May there be problems with the formats that the RenderTexture and Texture2D I am trying to fill?
Complete Code
In the following I add the whole code I am using. The names of the variables slightly differ to the ones I used above but they do essentially the same:
/**
* This class continously converts the y and uv textures in
* YUV color space to a RGB texture, which can be used somewhere else
*/
public class YUV2RGBConverter : MonoBehaviour {
public Material yuv2rgbMat;
// Input textures, set these when they are available
[HideInInspector]
public Texture2D yTex;
[HideInInspector]
public Texture2D uvTex;
// Output, the converted textures
[HideInInspector]
public RenderTexture rgbRenderTex;
[HideInInspector]
public Texture2D rgbTex;
[HideInInspector]
public Color32[] rawRgbData;
/// Describes how often per second the image should be transferred to the CPU
public float GPUTransferRate = 1.0f;
private float timeSinceLastGPUTransfer = 0.0f;
private int width;
private int height;
/**
* Initializes the used textures
*/
void Start () {
updateSize (width, height);
}
/**
* Depending on the sizes of the texture, creating the needed textures for this class
*/
public void updateSize(int width, int height)
{
// Generate the input textures
yTex = new Texture2D(width / 4, height, TextureFormat.RGBA32, false);
uvTex = new Texture2D ((width / 2) * 2 / 4, height / 2, TextureFormat.RGBA32, false);
// Generate the output texture
rgbRenderTex = new RenderTexture(width, height, 0);
rgbRenderTex.antiAliasing = 0;
rgbTex = new Texture2D (width, height, TextureFormat.RGBA32, false);
// Set to shader
yuv2rgbMat.SetFloat("_TexWidth", width);
yuv2rgbMat.SetFloat("_TexHeight", height);
}
/**
* Sets the y and uv textures to some dummy data
*/
public void fillYUWithDummyData()
{
// Set the y tex everywhere to time rest
float colorValue = (float)Time.time - (float)((int)Time.time);
for (int y = 0; y < yTex.height; y++) {
for (int x = 0; x < yTex.width; x++) {
Color yColor = new Color (colorValue, colorValue, colorValue, colorValue);
yTex.SetPixel (x, y, yColor);
}
}
yTex.Apply ();
// Set the uv tex colors
for (int y = 0; y < uvTex.height; y++) {
for (int x = 0; x < uvTex.width; x++) {
int firstXCoord = 2 * x;
int secondXCoord = 2 * x + 1;
int yCoord = y;
float firstXRatio = (float)firstXCoord / (2.0f * (float)uvTex.width);
float secondXRatio = (float)secondXCoord / (2.0f * (float)uvTex.width);
float yRatio = (float)y / (float)uvTex.height;
Color uvColor = new Color (firstXRatio, yRatio, secondXRatio, yRatio);
uvTex.SetPixel (x, y, uvColor);
}
}
uvTex.Apply ();
}
/**
* Continuously convert y and uv texture to rgb texture with custom yuv2rgb shader
*/
void Update () {
// Draw to it with the yuv2rgb shader
yuv2rgbMat.SetTexture ("_YTex", yTex);
yuv2rgbMat.SetTexture ("_UTex", uvTex);
Graphics.Blit (null, rgbRenderTex, yuv2rgbMat);
// Only scan once per second
if (timeSinceLastGPUTransfer > 1 / GPUTransferRate) {
timeSinceLastGPUTransfer = 0;
// Fetch its pixels and set it to rgb texture
RenderTexture.active = rgbRenderTex;
rgbTex.ReadPixels (new Rect (0, 0, width, height), 0, 0);
rgbTex.Apply ();
RenderTexture.active = null;
rawRgbData = rgbTex.GetPixels32 ();
} else {
timeSinceLastGPUTransfer += Time.deltaTime;
}
}
}
Ok, sorry that I have to answer my question on my own. The solution is very easy:
The width and height property that I was using in this line:
rgbTex.ReadPixels (new Rect (0, 0, width, height), 0, 0);
where not initialized, so they where 0.
I just had to add those lines to the updateSize function:
this.width = width;
this.height = height;