Xamarin:OpenTK OpenGL Android 崩溃

Xamarin : OpenTK OpenGL Android crash

我正在尝试创建一个 iOS/Android 演示应用程序,它显示 Xamarin OpenTK/OpenGl 在屏幕上渲染一些立方体。在 iOS 中一切正常,但在 Android 中的相同代码只是崩溃。这是基于他们的一些演示代码的完全共享代码。

我没有得到有关崩溃的任何信息。

using OpenTK.Graphics.ES20;
using Xamarin.Forms;
using OpenTK.Graphics;
using OpenTK;
using System.Reflection;
using System.IO;
using System.Diagnostics;
using System;

namespace GLDemo
{
    public class App
    {
        public static Page GetMainPage ()
        {   
            return new OpenGLPage ();
        }
    }

    public class OpenGLPage : ContentPage
    {

        uint positionSlot;
        uint colorSlot;
        uint projectionSlot;
        uint modelViewSlot;

        uint colorRenderBuffer;
        uint depthBuffer;

        // cube verticies 
        Vector3[] Verticies = new Vector3[] { 
            new Vector3 (1.0f, -1.0f, 1.0f),
            new Vector3 (1.0f, 1.0f, 1.0f),
            new Vector3 (-1.0f, 1.0f, 1.0f),
            new Vector3(-1.0f, -1.0f, 1.0f),
            new Vector3(1.0f, -1.0f, -1.0f),
            new Vector3(1.0f, 1.0f, -1.0f),
            new Vector3(-1.0f, 1.0f, -1.0f),
            new Vector3(-1.0f, -1.0f, -1.0f)};

        Vector4[] Colors = new Vector4[]{
            new Vector4(0.0f, 0.0f, 0.0f, 1.0f),
            new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
            new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
            new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
            new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
            new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
            new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
            new Vector4(1.0f, 1.0f, 1.0f, 1.0f)};

        byte[] Indices = new byte [] 
        { 0, 1, 2,
            2, 3, 0,
            4, 6, 5,
            4, 7, 6,
            2, 7, 3,
            7, 6, 2,
            0, 4, 1,
            4, 1, 5,
            6, 2, 1,
            1, 6, 5,
            0, 3, 7,
            0, 7, 4};

        public OpenGLPage ()
        {
            Title = "OpenGL";
            var view = new OpenGLView { HasRenderLoop = true };
            var toggle = new Xamarin.Forms.Switch { IsToggled = true };
            var button = new Button { Text = "Display" };

            view.HeightRequest = 300;
            view.WidthRequest = 300;

            bool initialize = false;

            float rotation = 0.0f;
            float translation = 0.0f;
            bool goingRight = true;

            view.OnDisplay = r => {

                if(!initialize){
                    SetupDepthBuffer ();
                    SetupRenderBuffers ();
                    SetupFrameBuffer ();
                    CompileShaders ();
                    SetupVBOs ();
                    initialize = true;
                }

                GL.ClearColor(0.0f,0.0f,0.0f,1.0f);
                GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
                GL.Enable(EnableCap.DepthTest);

                rotation += 0.02f;
                if(goingRight){
                    translation += 0.01f;
                    if(translation > 1.0f){
                        goingRight = false;
                    }
                } else{
                    translation -= 0.01f;
                    if(translation < -1.0f){
                        goingRight = true;
                    }
                }

                for(int i = 0; i < 3; i++){
                    float h = 4.0f * (float)view.Height / (float)view.Width;
                    Matrix4[] projection = new Matrix4[]{ Matrix4.CreatePerspectiveOffCenter(-2, 2, -h/2, h/2, 4, 10) };
                    GL.UniformMatrix4 ((int)projectionSlot, false, ref projection[0]);

                    Matrix4[] modelView = new Matrix4[]{ Matrix4.CreateRotationX(rotation) * Matrix4.CreateRotationY(rotation) * Matrix4.CreateRotationZ(rotation) * Matrix4.CreateTranslation (translation - i * 3.5f + 4.0f, (float)(-1^i) * translation, -7 + translation) };
                    GL.UniformMatrix4 ((int)modelViewSlot, false, ref modelView [0]);

                    GL.DrawElements(BeginMode.Triangles, Indices.Length, DrawElementsType.UnsignedByte, 0);
                }
            };

            toggle.Toggled += (s, a) => {
                view.HasRenderLoop = toggle.IsToggled;
            };
            button.Clicked += (s, a) => view.Display ();

            var stack = new StackLayout { 
                Padding = new Size (20, 20),
                Children = {view, toggle, button}
            };

            Content = stack;


        }

        void SetupRenderBuffers(){
            GL.GenRenderbuffers (1, out colorRenderBuffer);
            GL.BindBuffer (BufferTarget.ArrayBuffer, colorRenderBuffer);
            GL.RenderbufferStorage (RenderbufferTarget.Renderbuffer, RenderbufferInternalFormat.Rgba4, 300, 300);
        }

        void SetupFrameBuffer(){
            uint frameBuffer;
            GL.GenFramebuffers (1, out frameBuffer);
            GL.BindFramebuffer (FramebufferTarget.Framebuffer, frameBuffer);
            GL.FramebufferRenderbuffer (FramebufferTarget.Framebuffer, FramebufferSlot.ColorAttachment0,
                RenderbufferTarget.Renderbuffer, colorRenderBuffer);
            GL.FramebufferRenderbuffer (FramebufferTarget.Framebuffer, FramebufferSlot.DepthAttachment,
                RenderbufferTarget.Renderbuffer, depthBuffer);
        }

        void SetupDepthBuffer(){
            GL.GenRenderbuffers (1, out depthBuffer);
            GL.BindRenderbuffer (RenderbufferTarget.Renderbuffer, depthBuffer);
            GL.RenderbufferStorage (RenderbufferTarget.Renderbuffer, RenderbufferInternalFormat.DepthComponent16, 300, 300);
        }

        uint CompileShader(string shaderName, ShaderType shaderType){
            string prefix;

            #if __IOS__ 
            prefix = "GLDemo.iOS.";
            #endif
            #if __ANDROID__
            prefix = "GLDemo.Android.";
            #endif

            var assembly = typeof(App).GetTypeInfo ().Assembly;


            foreach (var res in assembly.GetManifestResourceNames())
                System.Diagnostics.Debug.WriteLine("found resource: " + res);

            Stream stream = assembly.GetManifestResourceStream (prefix + shaderName + ".glsl");

            string shaderString;

            using (var reader = new StreamReader (stream)) {
                shaderString = reader.ReadToEnd ();
            }
            Debug.WriteLine (shaderString);
            uint shaderHandle = (uint)GL.CreateShader (shaderType);
            GL.ShaderSource ((int)shaderHandle, shaderString);
            GL.CompileShader (shaderHandle);

            return shaderHandle;
        }

        void CompileShaders(){
            uint vertexShader = CompileShader ("SimpleVertex", ShaderType.VertexShader);
            uint fragmentShader = CompileShader ("SimpleFragment", ShaderType.FragmentShader);
            uint programHandle = (uint)GL.CreateProgram ();
            GL.AttachShader (programHandle, vertexShader);
            GL.AttachShader (programHandle, fragmentShader);
            GL.LinkProgram (programHandle);
            GL.UseProgram (programHandle);

            positionSlot = (uint)GL.GetAttribLocation (programHandle, "Position");
            colorSlot = (uint)GL.GetAttribLocation (programHandle, "SourceColor");
            projectionSlot = (uint)GL.GetUniformLocation (programHandle, "Projection");
            modelViewSlot = (uint)GL.GetUniformLocation (programHandle, "Modelview");

            GL.EnableVertexAttribArray (positionSlot);
            GL.EnableVertexAttribArray (colorSlot);
            GL.EnableVertexAttribArray (projectionSlot);
            GL.EnableVertexAttribArray (modelViewSlot);

        }

        void SetupVBOs(){
            uint vertexBuffer;
            GL.GenBuffers (1, out vertexBuffer);
            GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBuffer);
            GL.BufferData (BufferTarget.ArrayBuffer,(IntPtr)(Vector3.SizeInBytes * Verticies.Length), Verticies, BufferUsage.StaticDraw);
            GL.VertexAttribPointer (positionSlot, 3, VertexAttribPointerType.Float, false, Vector3.SizeInBytes, 0);

            uint colorBuffer;
            GL.GenBuffers (1, out colorBuffer);
            GL.BindBuffer (BufferTarget.ArrayBuffer, colorBuffer);
            GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(Vector4.SizeInBytes * Colors.Length), Colors, BufferUsage.StaticDraw);
            GL.VertexAttribPointer (colorSlot, 4, VertexAttribPointerType.Float, false, Vector4.SizeInBytes, 0);

            uint indexBuffer;
            GL.GenBuffers (1, out indexBuffer);
            GL.BindBuffer (BufferTarget.ElementArrayBuffer, indexBuffer);
            GL.BufferData (BufferTarget.ElementArrayBuffer,(IntPtr)(sizeof(byte) * Indices.Length), Indices, BufferUsage.StaticDraw);
        }
    }
}

更新

我认为这与 Android 和 VBO 有关。已知问题?谁知道怎么解决

更新 2

更新为

void SetupVBOs(){
            uint vertexBuffer;
            GL.GenBuffers (1, out vertexBuffer);
            GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBuffer);
            GL.BufferData (BufferTarget.ArrayBuffer,(IntPtr)(Vector3.SizeInBytes * Verticies.Length), Verticies, BufferUsage.StaticDraw);
            GL.VertexAttribPointer (positionSlot, 3, VertexAttribPointerType.Float, false, Vector3.SizeInBytes, 0);

            uint colorBuffer;
            GL.GenBuffers (1, out colorBuffer);
            GL.BindBuffer (BufferTarget.ArrayBuffer, colorBuffer);
            GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(Vector4.SizeInBytes * Colors.Length), Colors, BufferUsage.StaticDraw);
            GL.VertexAttribPointer (colorSlot, 4, VertexAttribPointerType.Float, false, Vector4.SizeInBytes, 0);
        }

我试试

GL.DrawElements(BeginMode.Triangles, Indices.Length, DrawElementsType.UnsignedByte, Indices);

它在设备上崩溃[​​=15=]

我试试

            unsafe
            {
                fixed (byte* ptr = Indices)
                {
                    GL.DrawElements(BeginMode.Triangles, Indices.Length, DrawElementsType.UnsignedByte, new IntPtr(ptr));

                }
            }

没有任何渲染错误

[Adreno-EGLSUB]:本机缓冲区无效。队列缓冲区失败 [Adreno-EGLSUB]:本机缓冲区为 NULL

出于某种原因,当我们将 IBO(索引缓冲区对象)与 Xamarin.Forms + OpenGL 一起使用时,Android 会崩溃。而是将索引信息传递给 DrawElements。
void SetupVBOs(){
    uint vertexBuffer;
    GL.GenBuffers (1, out vertexBuffer);
    GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBuffer);
    GL.BufferData (BufferTarget.ArrayBuffer,(IntPtr)(Vector3.SizeInBytes * Verticies.Length), Verticies, BufferUsage.StaticDraw);
    GL.VertexAttribPointer (positionSlot, 3, VertexAttribPointerType.Float, false, Vector3.SizeInBytes, 0);

    uint colorBuffer;
    GL.GenBuffers (1, out colorBuffer);
    GL.BindBuffer (BufferTarget.ArrayBuffer, colorBuffer);
    GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(Vector4.SizeInBytes * Colors.Length), Colors, BufferUsage.StaticDraw);
    GL.VertexAttribPointer (colorSlot, 4, VertexAttribPointerType.Float, false, Vector4.SizeInBytes, 0);
}

编辑

过去一周左右,我重新访问了一些 OpenTK,我想通了!

GL.DrawElements()是用来传入索引数据的,如果你更喜欢使用IBO,按照你展示的那样绑定它,而不是使用

GL.DrawArrays(BeginMode.Triangles, 0, Indices.Length);

我知道这个线程有点旧,但我已经解决了这个问题几天,最后通过将 DrawElements 调用的最后一个参数更改为 IntPtr.Zero 解决了这个问题:

GL.DrawElements(BeginMode.Triangles, Indices.Length, DrawElementsType.UnsignedByte, IntPtr.Zero);