Search code examples
c#androidxamarinopentk

Cannot get colors to render in GLSL Xamarin OpenGLView contol


Can anyone please help with the following?

Using Visual Studio 2019 Xamarin Forms project I am trying to display a rotating red square in the Android emulator. I have it working but my square is black:

enter image description here

Here's the code of my MainPage.xaml.cs (am using ES20 as ES10 does not have the OpenGL functions I need):

using OpenTK.Graphics.ES20;
using System;
using Xamarin.Forms;

namespace OGLMobile
{
    public partial class MainPage : ContentPage
    {
        private float red = 1.0f;
        private float green = 0.0f;
        private float blue = 0.0f;

        private const float halfWidth = 0.2f;
        private float[] m_vertex_buffer_data = { 0.5f - halfWidth, 0.5f - halfWidth, 0.0f,
                                                 0.5f + halfWidth, 0.5f - halfWidth, 0.0f,
                                                 0.5f + halfWidth, 0.5f + halfWidth, 0.0f,
                                                 0.5f - halfWidth, 0.5f + halfWidth, 0.0f };

        private string[] m_szVertexShader = null;
        private string[] m_szFragmentShader = null;

        private int m_nProgram = -1;
        private int m_nVertexShaderHandle = -1;
        private int m_nFragmentShaderHandle = -1;

        private uint[] indices = { 0, 1, 2, 0, 2, 3 };

        private int m_nVertexBuffer = -1;

        bool m_bOGLParametersSet = false;

        private void RotateSquare(float radians, float xRotationCentre, float yRotationCentre)
        {
            int[] nBaseIndices = { 0, 3, 6, 9 };

            for (int nVertex = 0; nVertex <= 3 ; nVertex++)
            {
                int nIndex1 = nBaseIndices[nVertex];
                int nIndex2 = nIndex1 + 1;

                float offsetX = m_vertex_buffer_data[nIndex1] - xRotationCentre;
                float offsetY = m_vertex_buffer_data[nIndex2] - yRotationCentre;

                double xRotated = offsetX * Math.Cos(radians) - offsetY * Math.Sin(radians);
                double yRotated = offsetX * Math.Sin(radians) + offsetY * Math.Cos(radians);

                m_vertex_buffer_data[nIndex1] = (float)xRotated + xRotationCentre;
                m_vertex_buffer_data[nIndex2] = (float)yRotated + yRotationCentre;
            }

            GL.BindBuffer(BufferTarget.ArrayBuffer, m_nVertexBuffer);
            GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, new IntPtr(m_vertex_buffer_data.Length * sizeof(float)), m_vertex_buffer_data);
        }

        public MainPage()
        {
            Title = "OpenGL";
            var view = new OpenGLView { HasRenderLoop = true };
            var toggle = new Switch { IsToggled = true };

            m_szVertexShader = new string[1];
            m_szFragmentShader = new string[1];

            view.HeightRequest = 300;
            view.WidthRequest = 300;
            
            GL.Viewport(0, 0, 300, 300);

            view.OnDisplay = r =>
            {
                if(!m_bOGLParametersSet) // Do this only on first rendering
                {
                    CreateShader();

                    m_bOGLParametersSet = true;

                    GL.UseProgram(m_nProgram);

                    GL.GenBuffers(1, out m_nVertexBuffer);

                    GL.BindBuffer(BufferTarget.ArrayBuffer, m_nVertexBuffer);

                    GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(m_vertex_buffer_data.Length * sizeof(float)), m_vertex_buffer_data, BufferUsage.StaticDraw);
                }

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

                RotateSquare(0.0174533f, 0.5f, 0.5f);

                GL.EnableVertexAttribArray(0);
                GL.BindBuffer(BufferTarget.ArrayBuffer, m_nVertexBuffer);


                GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 0, 0);

                GL.DrawElements(BeginMode.Triangles, indices.Length, DrawElementsType.UnsignedInt, indices);

                GL.DisableVertexAttribArray(0);

                
            };

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

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

            Content = stack;
        }

        private void SetShaderSource()
        {
            m_szVertexShader[0] = "void main()" +
                                  "{" +
                                    "gl_Position = ftransform();" +
                                  "}";

            m_szFragmentShader[0] = "void main()" +
                                    "{" +
                                        "gl_FragColor = vec4(1.0,0.0,0.0,1.0);" +
                                    "}";
        }


        private void CreateShader()
        {
            SetShaderSource();

            int nVertexShaderSourceLength = m_szVertexShader[0].Length;
            int nFragmentShaderLength = m_szFragmentShader[0].Length;

            m_nVertexShaderHandle = GL.CreateShader(ShaderType.VertexShader);
            m_nFragmentShaderHandle = GL.CreateShader(ShaderType.FragmentShader);

            GL.ShaderSource(m_nVertexShaderHandle, 1, m_szVertexShader, new int[] { nVertexShaderSourceLength });
            GL.ShaderSource(m_nFragmentShaderHandle, 1, m_szFragmentShader, new int[] { nVertexShaderSourceLength });

            GL.CompileShader(m_nVertexShaderHandle);
            GL.CompileShader(m_nFragmentShaderHandle);

            string szVertexShaderLog = GL.GetShaderInfoLog(m_nVertexShaderHandle);
            string szFragmentShaderLog = GL.GetShaderInfoLog(m_nFragmentShaderHandle);

            m_nProgram = GL.CreateProgram();

            GL.AttachShader(m_nProgram, m_nVertexShaderHandle);
            GL.AttachShader(m_nProgram, m_nFragmentShaderHandle);

            GL.LinkProgram(m_nProgram);
        }
    }
}

My shaders must be incorrect, I get the same output if I replace them with junk strings, so the system must be defaulting to something.

I do have compile errors when I call GetShaderInfoLog, but my shaders are so trivial that I can't see the issue:

enter image description here

Thanks very much for any help, (been stuck on this for a while now. Have done plenty of OpenGL on desktop and with WebGL before, but not having any luck yet with mobile).

ADDITION: Thanks very much for you replies. I have replaced tha shaders with compliant ones, unfortunately I still have a black square:

(In the image it says mediump, I have also tried highp and lowp but I still get the same results).

enter image description here

I now get a different error on compilation of the vertex shader, and no error for the fragment shader - although I do have one warning for the latter:

enter image description here

Thank you for any further advice you may be able to offer.


Solution

  • These shaders do not meet any GLSL ES specification. See OpenGL ES Shading Language 1.00 Specification respectively. OpenGL ES Shading Language 3.00 Specification.

    See a working shader with a vertex shader:

    attribute vec3 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 1.0);
    }
    

    and fragment shader

    precision mediump float;
    
    void main()
    {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
    

    I recommend to verify if the shader compiles with out errors:

    GL.CompileShader(m_nVertexShaderHandle);
    string infoLogFrag = GL.GetShaderInfoLog(m_nVertexShaderHandle);
    if (infoLogFrag != System.String.Empty)
    {
        System.Console.WriteLine(infoLogFrag);
    }
    
    GL.CompileShader(m_nFragmentShaderHandle);
    string infoLogVert = GL.GetShaderInfoLog(m_nFragmentShaderHandle);
    if (infoLogVert != System.String.Empty)
    {
        System.Console.WriteLine(infoLogVert);
    }
    

    And the program links without errors:

    GL.LinkProgram(m_nProgram);
    string infoLogProg = GL.GetProgramInfoLog(m_nProgram );
    if (infoLogProg != System.String.Empty)
    {
        System.Console.WriteLine(infoLogProg);
    }