OpenGL(使用OpenTK)纹理不渲染(甚至不是白色的rect)

xt0899hw  于 2023-08-04  发布在  其他
关注(0)|答案(2)|浏览(158)

我正在对我大学的一个游戏引擎进行大修,在处理OpenGL渲染时遇到了一堵墙。我使用OpenTK和GLFW.NET来渲染窗口。(我最近切换到OpenTK,并没有费心去切换到他们的GLFW而不是GLFW.NET,但我可能会在未来这么做)
我将在这里发布的代码是从包含类中取出的,但我也测试过它,但它仍然不能工作。这不是GLFW或链接procAddress的问题,因为我已经设法使用窗口上下文渲染了一个三角形。
在绘制图像的当前试验中,没有GL错误代码。
GLContext.cs
这由GameProcess调用,在创建窗口后使用Run()方法。

private Texture2D tex;

public ShaderProgram shader;
public VertexArrayObject vao;
public VertexBufferObject vbo;
public ElementBufferObject ebo;

Matrix4 viewMatrix;
Matrix4 orthoMatrix;

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

public override void Run() {
    // perform gl setup
    GL.Enable(EnableCap.DepthTest);
    GL.Enable(EnableCap.Multisample);
    //GL.Enable(EnableCap.Texture2D);
    GL.Enable(EnableCap.Blend);

    GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    tex = new Texture2D(new Bitmap("Assets/Sprites/Test.png")); // This image exists
    tex.Load();

    vao = new VertexArrayObject();
    vao.Bind();

    float[] vertices = new float[]
    {
        // Position         Texture coordinates
         0.5f,  0.5f, 0.0f,
         0.5f, -0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        -0.5f,  0.5f, 0.0f,
    };

    float[] texCoords = new float[]
    {
         1.0f, 1.0f, // top right
         1.0f, 0.0f, // bottom right
         0.0f, 0.0f, // bottom left
         0.0f, 1.0f  // top left
    };

    // Generates Vertex Buffer Object and links it to vertices
    VertexBufferObject vbo = new VertexBufferObject(vertices, texCoords);
    // Generates Element Buffer Object and links it to indices
    ElementBufferObject ebo = new ElementBufferObject(indices);

    shader = new ShaderProgram();
    shader.Use();

    // Links VBO attributes such as coordinates and colors to VAO
    vao.LinkAttrib(vbo, shader.GetAttribLocation("aPos"), 3, VertexAttribPointerType.Float, 5 * sizeof(float), 0);
    vao.LinkAttrib(vbo, shader.GetAttribLocation("aTex"), 2, VertexAttribPointerType.Float, 5 * sizeof(float), 3 * sizeof(float));
    // Unbind all to prevent accidentally modifying them
    vao.Unbind();
    vbo.Unbind();
    ebo.Unbind();

    viewMatrix = Matrix4.CreateTranslation(0.0f, 0.0f, -3.0f);

    Vector2 widthHeight = GLContext.Instance.windowDimensions.Clone();
    widthHeight.x /= GLContext.Instance.ratios.x;
    widthHeight.y /= GLContext.Instance.ratios.y;
    orthoMatrix = Matrix4.CreateOrthographicOffCenter(0f, widthHeight.x, 0f, widthHeight.y, 0f, 1000f);

    Glfw.Time = 0.0D;
    do {
        if (vsync || Time.time - lastFrameTime > 1000 / targetFrameRate) {
            lastFrameTime = Time.time;

            frameCount++;
            if (Time.time - lastFPSTime > 1000) {
                lastFPS = (int)(frameCount / ((Time.time - lastFPSTime) / 1000.0f));
                lastFPSTime = Time.time;
                frameCount = 0;
            }

            UpdateMouseInput();
            game.Step();
            soundSystem.Step();

            ResetHitCounters();

            // TODO move to different thread
            Display();

            Time.newFrame();
            Glfw.PollEvents();
        }
    } while (!Glfw.WindowShouldClose(window));

    // the process is being terminated
    Close();
}

public override void Display() {
    // Specify the color of the background
    GL.ClearColor(0.07f, 0.13f, 0.17f, 1.0f);
    // Clean the back buffer and assign the new color to it
    GL.Clear(ClearBufferMask.ColorBufferBit);
    
    //game.Render();
    vao.Bind();
    tex.Bind();
    shader.Use();
    
    //Matrix4 model = transform.matrix;
    shader.SetInt("tex0", 0);
    shader.SetVector4("color", new Vector4(1f,1f,1f,1f)); // TODO change from Color32 to color clamp (0-1)
    //shader.SetMatrix4("model", model);
    shader.SetMatrix4("view", viewMatrix);
    shader.SetMatrix4("ortho", orthoMatrix);

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

    tex.Unbind();
    vao.Unbind();

    Console.WriteLine(GL.GetError());
    
    // Swap the back buffer with the front buffer
    Glfw.SwapBuffers(window);
}

字符串
Texture2D.cs

using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using OpenTK.Graphics.OpenGL4;

#pragma warning disable CA1416 // Validate platform compatibility
public unsafe class Texture2D : AssetInstance
{
    private static Texture2D? lastBound = null;

    internal Bitmap bitmap;
    private uint glTex = 0;
    private TextureUnit slot;

    public int width
    {
        get { return bitmap.Width; }

    }
    public int height
    {
        get { return bitmap.Height; }
    }

    private bool _wrap = false;
    public bool wrap
    {
        get { return _wrap; }
        set {
            // cannot change texture as it was not created
            if (glTex == 0) return;

            _wrap = value;

            // modify tex wrap
            Bind();
            GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapS,
                value ? new int[] { (int) All.Repeat } : new int[] { (int) All.ClampToEdge });
            GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapT,
                value ? new int[] { (int) All.Repeat } : new int[] { (int) All.ClampToEdge });
            Unbind();
        }
    }

    public Texture2D(int width, int height) : this(new Bitmap(width, height)) {
        if (width == 0 || height == 0) throw new ArgumentException("Width and Height need to be greater than 0!");
    }

    public Texture2D(Bitmap bitmap) {
        if (bitmap == null || bitmap.Width == 0 || bitmap.Height == 0) throw new ArgumentException("Width and Height need to be greater than 0!");

        this.bitmap = bitmap;
    }

    /// <summary>
    /// Creates a GL Texture
    /// </summary>
    internal void CreateTexture(TextureUnit slot = TextureUnit.Texture0) {
        if (glTex != 0)
            DestroyTexture();

        this.slot = slot;

        bitmap ??= new Bitmap(64, 64);

        fixed (uint* pointer = &glTex)
            GL.GenTextures(1, pointer);

        GL.ActiveTexture(slot);
        GL.BindTexture(TextureTarget.Texture2D, glTex);

        if (GameProcess.Main.settings.PixelArt) {
            GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, new int[] { (int) TextureMinFilter.LinearMipmapNearest });
            GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, new int[] { (int) TextureMagFilter.Nearest });
        }
        else {
            GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, new int[] { (int) TextureMinFilter.LinearMipmapLinear });
            GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, new int[] { (int) TextureMagFilter.Linear });
        }

        if (wrap) {
            GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, new int[] { (int) TextureWrapMode.Repeat });
            GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, new int[] { (int) TextureWrapMode.Repeat });
        }
        else {
            GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, new int[] { (int) TextureWrapMode.ClampToEdge });
            GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, new int[] { (int) TextureWrapMode.ClampToEdge });
        }

        UpdateTexture();

        Unbind();
    }

    /// <summary>
    /// Updates the GL Texture with glTexImage2D
    /// </summary>
    internal void UpdateTexture() {
        BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

        //GL.BindTexture(TextureTarget.Texture2D, glTex);
        GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0,
            OpenTK.Graphics.OpenGL4.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
        GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);

        bitmap.UnlockBits(data);
    }

    /// <summary>
    /// Destroys the GL Texture
    /// </summary>
    internal void DestroyTexture() {
        fixed (uint* pointer = &glTex)
            GL.DeleteTextures(1, pointer);

        glTex = 0;
    }

    internal void Bind() {
        if (lastBound == this) return;
        lastBound = this;
        GL.ActiveTexture(slot);
        GL.BindTexture(TextureTarget.Texture2D, glTex);
    }

    internal void Unbind()
    {
        if (lastBound != this) return;
        GL.BindTexture(TextureTarget.Texture2D, 0);
        lastBound = null;
    }

    internal override void Load() {
        CreateTexture();
    }

    internal override void Unload() {
        DestroyTexture();
    }
}
#pragma warning restore CA1416


VertexArrayObject.cs

using OpenTK.Graphics.OpenGL4;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

// https://github.com/VictorGordan/opengl-tutorials/blob/main/YoutubeOpenGL%206%20-%20Textures/VAO.cpp
// translated from c++

/// <summary>
/// Vertex Array Object
/// </summary>
internal class VertexArrayObject
{
    private int[] id = new int[1];

    public VertexArrayObject() {
        GL.GenVertexArrays(1, id);
    }

    // Links a VBO Attribute such as a position or color to the VAO
    public void LinkAttrib(VertexBufferObject vbo, int layout, int numComponents, VertexAttribPointerType type, int stride, nint offset) {
        vbo.Bind();
        GL.VertexAttribPointer(layout, numComponents, type, false, stride, offset);
        GL.EnableVertexAttribArray(layout);
        vbo.Unbind();
    }

    // Binds the VAO
    public void Bind() {
        GL.BindVertexArray(id[0]);
    }

    // Unbinds the VAO
    public void Unbind() {
        GL.BindVertexArray(0);
    }

    // Deletes the VAO
    public void Delete() {
        GL.DeleteVertexArrays(1, id);
    }
}


VertexBufferObject.cs

using OpenTK.Graphics.OpenGL4;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

/// <summary>
/// Vertex Buffer Object
/// </summary>
internal class VertexBufferObject
{
    private int[] id = new int[1];

    // Constructor that generates a Vertex Buffer Object and links it to vertices
    public VertexBufferObject(float[] vertices, float[] texCoords) {
        int size = (vertices.Length + texCoords.Length) * sizeof(float); // not sure if it's required to specify sizeof(float)
        
        GL.GenBuffers(1, id);
        GL.BindBuffer(BufferTarget.ArrayBuffer, id[0]);
        GL.BufferData(BufferTarget.ArrayBuffer, size, IntPtr.Zero, BufferUsageHint.StaticDraw);
        GL.BufferSubData(BufferTarget.ArrayBuffer, 0, vertices.Length * sizeof(float), vertices);
        GL.BufferSubData(BufferTarget.ArrayBuffer, vertices.Length, texCoords.Length * sizeof(float), texCoords);
    }

    // Binds the VBO
    public void Bind() {
        GL.BindBuffer(BufferTarget.ArrayBuffer, id[0]);
    }

    // Unbinds the VBO
    public void Unbind() {
        GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
    }

    // Deletes the VBO
    public void Delete() {
        GL.DeleteBuffers(1, id);
    }
}


ElementBufferObject.cs

using OpenTK.Graphics.OpenGL4;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

/// <summary>
/// ElementBufferObject
/// </summary>
internal class ElementBufferObject
{
    private int[] id = new int[1];

    // Constructor that generates a Elements Buffer Object and links it to indices
    public ElementBufferObject(uint[] indices) {
        int size = indices.Length * sizeof(uint);

        GL.GenBuffers(1, id);
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, id[0]);
        GL.BufferData(BufferTarget.ElementArrayBuffer, size, indices, BufferUsageHint.StaticDraw);
    }

    // Binds the EBO
    public void Bind() {
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, id[0]);
    }

    // Unbinds the EBO
    public void Unbind() {
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
    }

    // Deletes the EBO
    public void Delete() {
        GL.DeleteBuffers(1, id);
    }
}


ShaderProgram.cs(摘自:[https://github.com/opentk/LearnOpenTK/blob/master/Common/Shader.cs])

using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

// https://github.com/opentk/LearnOpenTK/blob/master/Common/Shader.cs
public class ShaderProgram
{
    public static readonly string[] defaultShaders = new string[] {
        "Namespace.To.Shader.Folder.defaultVertex.shader", // these exist in the assembly
        "Namespace.To.Shader.Folder.defaultFrag.shader",
    };

    public static string GetEmbeddedResource(string filename)
    {
        var assembly = Assembly.GetExecutingAssembly();

        using (Stream stream = assembly.GetManifestResourceStream(filename))
        using (StreamReader reader = new StreamReader(stream))
        {
            string result = reader.ReadToEnd();
            return result;
        }
    }

    public readonly int Handle;

    private readonly Dictionary<string, int> _uniformLocations;

    public ShaderProgram() : this(defaultShaders[0], defaultShaders[1], true) { }

    // This is how you create a simple shader.
    // Shaders are written in GLSL, which is a language very similar to C in its semantics.
    // The GLSL source is compiled *at runtime*, so it can optimize itself for the graphics card it's currently being used on.
    // A commented example of GLSL can be found in shader.vert.
    public ShaderProgram(string vertPath, string fragPath, bool inAssembly = true)
    {
        // There are several different types of shaders, but the only two you need for basic rendering are the vertex and fragment shaders.
        // The vertex shader is responsible for moving around vertices, and uploading that data to the fragment shader.
        //   The vertex shader won't be too important here, but they'll be more important later.
        // The fragment shader is responsible for then converting the vertices to "fragments", which represent all the data OpenGL needs to draw a pixel.
        //   The fragment shader is what we'll be using the most here.

        // Load vertex shader and compile
        string shaderSource;
        
        if (inAssembly)
            shaderSource = GetEmbeddedResource(vertPath);
        else
            shaderSource = File.ReadAllText(vertPath);

        // GL.CreateShader will create an empty shader (obviously). The ShaderType enum denotes which type of shader will be created.
        var vertexShader = GL.CreateShader(ShaderType.VertexShader);

        // Now, bind the GLSL source code
        GL.ShaderSource(vertexShader, shaderSource);

        // And then compile
        CompileShader(vertexShader);

        // We do the same for the fragment shader.
        if (inAssembly)
            shaderSource = GetEmbeddedResource(fragPath);
        else
            shaderSource = File.ReadAllText(fragPath);

        var fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
        GL.ShaderSource(fragmentShader, shaderSource);
        CompileShader(fragmentShader);

        // These two shaders must then be merged into a shader program, which can then be used by OpenGL.
        // To do this, create a program...
        Handle = GL.CreateProgram();

        // Attach both shaders...
        GL.AttachShader(Handle, vertexShader);
        GL.AttachShader(Handle, fragmentShader);

        // And then link them together.
        LinkProgram(Handle);

        // When the shader program is linked, it no longer needs the individual shaders attached to it; the compiled code is copied into the shader program.
        // Detach them, and then delete them.
        GL.DetachShader(Handle, vertexShader);
        GL.DetachShader(Handle, fragmentShader);
        GL.DeleteShader(fragmentShader);
        GL.DeleteShader(vertexShader);

        // The shader is now ready to go, but first, we're going to cache all the shader uniform locations.
        // Querying this from the shader is very slow, so we do it once on initialization and reuse those values
        // later.

        // First, we have to get the number of active uniforms in the shader.
        GL.GetProgram(Handle, GetProgramParameterName.ActiveUniforms, out var numberOfUniforms);

        // Next, allocate the dictionary to hold the locations.
        _uniformLocations = new Dictionary<string, int>();

        // Loop over all the uniforms,
        for (var i = 0; i < numberOfUniforms; i++)
        {
            // get the name of this uniform,
            var key = GL.GetActiveUniform(Handle, i, out _, out _);

            // get the location,
            var location = GL.GetUniformLocation(Handle, key);

            // and then add it to the dictionary.
            _uniformLocations.Add(key, location);
        }
    }

    private static void CompileShader(int shader)
    {
        // Try to compile the shader
        GL.CompileShader(shader);

        // Check for compilation errors
        GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
        if (code != (int)All.True)
        {
            // We can use `GL.GetShaderInfoLog(shader)` to get information about the error.
            var infoLog = GL.GetShaderInfoLog(shader);
            throw new Exception($"Error occurred whilst compiling Shader({shader}).\n\n{infoLog}");
        }
    }

    private static void LinkProgram(int program)
    {
        // We link the program
        GL.LinkProgram(program);

        // Check for linking errors
        GL.GetProgram(program, GetProgramParameterName.LinkStatus, out var code);
        if (code != (int)All.True)
        {
            // We can use `GL.GetProgramInfoLog(program)` to get information about the error.
            throw new Exception($"Error occurred whilst linking Program({program})");
        }
    }

    // A wrapper function that enables the shader program.
    public void Use()
    {
        GL.UseProgram(Handle);
    }

    // The shader sources provided with this project use hardcoded layout(location)-s. If you want to do it dynamically,
    // you can omit the layout(location=X) lines in the vertex shader, and use this in VertexAttribPointer instead of the hardcoded values.
    public int GetAttribLocation(string attribName)
    {
        return GL.GetAttribLocation(Handle, attribName);
    }

    // Uniform setters
    // Uniforms are variables that can be set by user code, instead of reading them from the VBO.
    // You use VBOs for vertex-related data, and uniforms for almost everything else.

    // Setting a uniform is almost always the exact same, so I'll explain it here once, instead of in every method:
    //     1. Bind the program you want to set the uniform on
    //     2. Get a handle to the location of the uniform with GL.GetUniformLocation.
    //     3. Use the appropriate GL.Uniform* function to set the uniform.

    /// <summary>
    /// Set a uniform int on this shader.
    /// </summary>
    /// <param name="name">The name of the uniform</param>
    /// <param name="data">The data to set</param>
    public void SetInt(string name, int data)
    {
        GL.UseProgram(Handle);
        GL.Uniform1(_uniformLocations[name], data);
    }

    /// <summary>
    /// Set a uniform float on this shader.
    /// </summary>
    /// <param name="name">The name of the uniform</param>
    /// <param name="data">The data to set</param>
    public void SetFloat(string name, float data)
    {
        GL.UseProgram(Handle);
        GL.Uniform1(_uniformLocations[name], data);
    }

    /// <summary>
    /// Set a uniform Matrix4 on this shader
    /// </summary>
    /// <param name="name">The name of the uniform</param>
    /// <param name="data">The data to set</param>
    /// <remarks>
    ///   <para>
    ///   The matrix is transposed before being sent to the shader.
    ///   </para>
    /// </remarks>
    public void SetMatrix4(string name, Matrix4 data)
    {
        GL.UseProgram(Handle);
        GL.UniformMatrix4(_uniformLocations[name], true, ref data);
    }

    /// <summary>
    /// Set a uniform Vector3 on this shader.
    /// </summary>
    /// <param name="name">The name of the uniform</param>
    /// <param name="data">The data to set</param>
    public void SetVector3(string name, Vector3 data)
    {
        GL.UseProgram(Handle);
        GL.Uniform3(_uniformLocations[name], data);
    }

    /// <summary>
    /// Set a uniform Vector4 on this shader.
    /// </summary>
    /// <param name="name">The name of the uniform</param>
    /// <param name="data">The data to set</param>
    public void SetVector4(string name, Vector4 data)
    {
        GL.UseProgram(Handle);
        GL.Uniform4(_uniformLocations[name], data);
    }
}


我的顶点和碎片着色器是:
defaultVertex.shader

#version 330 core

// Positions/Coordinates
layout (location = 0) in vec3 aPos;
// Texture Coordinates
layout (location = 1) in vec2 aTex;

// Outputs the texture coordinates to the fragment shader
out vec2 texCoord;

uniform mat4 model; // disabled for debug purposes
uniform mat4 view;
uniform mat4 ortho;

void main()
{
    // Outputs the positions/coordinates of all vertices
    gl_Position = vec4(aPos, 1.0) /* model */* view * ortho;
    // Assigns the texture coordinates from the Vertex Data to "texCoord"
    texCoord = aTex;
}


defaultFrag.shader

#version 330 core

// Outputs colors in RGBA
out vec4 FragColor;

// Inputs the texture coordinates from the Vertex Shader
in vec2 texCoord;

// Gets the color
uniform vec4 color;
// Gets the Texture Unit from the main function
uniform sampler2D tex0;

void main()
{
    FragColor = texture(tex0, texCoord) * color;
}


我已经学习了基础知识,适度地理解了open gl的工作原理,以及我应该如何在过去的3天内使用它,所以我真的没有资格解决这个问题,如果这是一个愚蠢的简单错误,我也道歉,我的代码数量和它的缺乏整洁。
我还想补充一点,我在GLFW.NET中强制使用OpenGL 4

Glfw.WindowHint(Hint.ContextVersionMajor, 4);
Glfw.WindowHint(Hint.ContextVersionMinor, 4);


我使用的窗口是800 x800,当前输出的背景颜色是清晰的:x1c 0d1x的数据
这些是我用c++教程渲染的三角形:


我尝试过的:
在片段着色器中禁用颜色倍增
禁用在位置顶部相乘的所有矩阵
使用不同的顶点和/或索引
将out FragColor设置为平坦值

66bbxpm5

66bbxpm51#

顶点缓冲区的布局不是

x0, y0, z0, u0, v0, x1, y1, z1, u1, v1, ...

字符串
但事实如此

x0, y0, z0, y1, y1, z1, ..., u0, v0, u1, v1, ...


因此,您需要更改顶点属性规范:
第一个月
vao.LinkAttrib(vbo, shader.GetAttribLocation("aTex"), 2, VertexAttribPointerType.Float, 5 * sizeof(float), 3 * sizeof(float));

vao.LinkAttrib(vbo, shader.GetAttribLocation("aPos"), 3, VertexAttribPointerType.Float,
    3 * sizeof(float), 0);
vao.LinkAttrib(vbo, shader.GetAttribLocation("aTex"), 2, VertexAttribPointerType.Float, 
    2 * sizeof(float), vertices.Length * sizeof(float));


几何体的大小约为1像素,且位于窗口的左下角,因为顶点坐标在[-0.5,0.5]范围内,但您使用正交投影将体积从(0,0,0)投影到视口上的(widthHeight.xwidthHeight.y,1000 f)。更改投影矩阵:
orthoMatrix = Matrix4.CreateOrthographicOffCenter(0f, widthHeight.x, 0f, widthHeight.y, 0f, 1000f);

float aspect = (float)widthHeight.x / (float)widthHeight.y;
orthoMatrix = Matrix4.CreateOrthographicOffCenter(-aspect, aspect, -1.0f, 1.0f, -1.0f, 1.0f);

dddzy1tm

dddzy1tm2#

已经有几天了,头疼得很厉害,但我让它起作用了。有两个主要问题导致无法渲染:使用GL.Enable(EnableCap.DepthTest)似乎导致所有内容都无法呈现。使用VertexAttribPointer s的子缓冲区将不起作用,因为子缓冲区将tex坐标放置在顶点之后,而VertexAttribPointer似乎将顶点与每个顶点的tex坐标沿着使用,而不是所有顶点,然后是所有tex坐标。(我不确定是不是这样,但是把顶点和tex坐标放在同一条“线”(顶点)上对我来说是有效的)。
GL函数的执行顺序可能还有其他问题,但老实说,我不记得了...我将用这个答案将其标记为已解决,因此它不再被打开,并且我将把这段代码留在这里,以防有人(极不可能)需要它。

private readonly float[] _verticesComplete =
        {
            // Position         Texture coordinates
             0.5f,  0.5f, 0.0f, 1.0f, 1.0f, // top right
             0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
            -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
            -0.5f,  0.5f, 0.0f, 0.0f, 1.0f  // top left
        };

    private float[][] _vertices =
    {
        // Position         Texture coordinates
         new float[] {  0.5f,  0.5f, 0.0f },
         new float[] {  0.5f, -0.5f, 0.0f },
         new float[] { -0.5f, -0.5f, 0.0f },
         new float[] { -0.5f,  0.5f, 0.0f }
    };

    private readonly float[][] texCoords =
    {
        new float[] { 1.0f, 1.0f }, // top right
        new float[] { 1.0f, 0.0f }, // bottom right
        new float[] { 0.0f, 0.0f },// bottom left
        new float[] { 0.0f, 1.0f } // top left
    };

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

    private int _elementBufferObject;

    private int _vertexBufferObject;

    private int _vertexArrayObject;

    private ShaderProgram _shader;

    private Texture _texture;

    // We create a double to hold how long has passed since the program was opened.
    private double _time;

    // Then, we create two matrices to hold our view and projection. They're initialized at the bottom of OnLoad.
    // The view matrix is what you might consider the "camera". It represents the current viewport in the window.
    private Matrix4 _view;

    // This represents how the vertices will be projected. It's hard to explain through comments,
    // so check out the web version for a good demonstration of what this does.
    private Matrix4 _projection;

    public override void Run() {
        // perform gl setup
        //GL.Enable(EnableCap.DepthTest);
        GL.Enable(EnableCap.Multisample);
        GL.Enable(EnableCap.Blend);
        GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
        
        // TEST

        _vertexArrayObject = GL.GenVertexArray();
        GL.BindVertexArray(_vertexArrayObject);

        _texture = Texture.LoadFromFile("Assets/Sprites/Test.png");

        _vertices = _texture.GetVertices();

        var vertices = new List<float>();

        for (int i = 0; i < _vertices.Length; i++) {
            float[] vertex = new float[5];
            Array.Copy(_vertices[i], vertex, _vertices[i].Length);
            Array.Copy(texCoords[i], 0, vertex, _vertices[i].Length, texCoords[i].Length);
            vertices.AddRange(vertex);

            Console.WriteLine(_vertices[i][0] + ", " + _vertices[i][1] + ", " + _vertices[i][2]);
            Console.WriteLine(texCoords[i][0] + ", " + texCoords[i][1]);
            Console.WriteLine();
        }

        _vertexBufferObject = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
        GL.BufferData(BufferTarget.ArrayBuffer, vertices.Count * sizeof(float), vertices.ToArray(), BufferUsageHint.StaticDraw);

        _elementBufferObject = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
        GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);

        // shader.vert has been modified. Take a look at it after the explanation in OnRenderFrame.
        _shader = new ShaderProgram("UGXP.Core.Render.Shaders.shader.vert", "UGXP.Core.Render.Shaders.shader.frag");
        _shader.Use();

        var vertexLocation = _shader.GetAttribLocation("aPosition");
        GL.EnableVertexAttribArray(vertexLocation);
        GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);

        var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
        GL.EnableVertexAttribArray(texCoordLocation);
        GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));

        _texture.Use(TextureUnit.Texture0);

        _shader.SetInt("slot", 0);

        // For the view, we don't do too much here. Next tutorial will be all about a Camera class that will make it much easier to manipulate the view.
        // For now, we move it backwards three units on the Z axis.
        _view = Matrix4.CreateTranslation(0.0f, 0.0f, 0.0f);

        Vector2 widthHeight = Instance.windowSize.Clone();

        float orthoSize = 7f;
        float aspect = widthHeight.x / widthHeight.y;

        float heightUnits = orthoSize;
        float widthUnits = heightUnits * aspect;

        // after all the research
        // ortho size means the half of the total amount of GAME UNITS attributed to the height of the projection.
        // the width is calculated with the height times the aspect ratio
        // the height / width units are then placed as left right, bottom, top sizes of the projection, which esentially doubles the size.

        Console.WriteLine(widthUnits + "x" + heightUnits);

        
        _projection = Matrix4.CreateOrthographicOffCenter(-widthUnits, widthUnits, -heightUnits, heightUnits,  -1.0f, 1000.0f);

        //_projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(45f), Instance.windowDimensions.x/ (float) Instance.windowDimensions.y, 0.1f, 100.0f);

        // Now, head over to OnRenderFrame to see how we setup the model matrix.

        // ENDTEST

        Glfw.Time = 0.0D;
        do {
            if (vsync || Time.time - lastFrameTime > 1000 / targetFrameRate) {
                lastFrameTime = Time.time;

                frameCount++;
                if (Time.time - lastFPSTime > 1000) {
                    lastFPS = (int)(frameCount / ((Time.time - lastFPSTime) / 1000.0f));
                    lastFPSTime = Time.time;
                    frameCount = 0;
                }

                UpdateMouseInput();
                game.Step();
                soundSystem.Step();

                ResetHitCounters();

                Display();

                Time.newFrame();
                Glfw.PollEvents();
            }
        } while (!Glfw.WindowShouldClose(window));

        // the process is being terminated
        Close();
    }

    public override void Display() {
        // We add the time elapsed since last frame, times 4.0 to speed up animation, to the total amount of time passed.
        _time += Time.deltaTime;

        // We clear the depth buffer in addition to the color buffer.
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

        GL.BindVertexArray(_vertexArrayObject);

        _texture.Use(TextureUnit.Texture0);
        _shader.Use();

        // Finally, we have the model matrix. This determines the position of the model.
        var model = Matrix4.Identity * Matrix4.CreateRotationX((float)MathHelper.DegreesToRadians(0f));

        // Then, we pass all of these matrices to the vertex shader.
        // You could also multiply them here and then pass, which is faster, but having the separate matrices available is used for some advanced effects.

        // IMPORTANT: OpenTK's matrix types are transposed from what OpenGL would expect - rows and columns are reversed.
        // They are then transposed properly when passed to the shader. 
        // This means that we retain the same multiplication order in both OpenTK c# code and GLSL shader code.
        // If you pass the individual matrices to the shader and multiply there, you have to do in the order "model * view * projection".
        // You can think like this: first apply the modelToWorld (aka model) matrix, then apply the worldToView (aka view) matrix, 
        // and finally apply the viewToProjectedSpace (aka projection) matrix.
        _shader.SetMatrix4("model", model);
        _shader.SetVector4("color", new Vector4(1f,1f,1f,1f) /*new Vector4(.5f,.2f,.12f,1f)*/);
        _shader.SetMatrix4("view", Matrix4.LookAt(new Vector3(0,0,0), new Vector3(0,0,0) -Vector3.UnitZ, Vector3.UnitY));
        _shader.SetMatrix4("projection", _projection);

        GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);
        
        // Swap the back buffer with the front buffer
        Glfw.SwapBuffers(window);
    }

字符串
Texture类I不幸被删除,现在正在使用的Texture2D类要复杂得多,超出了这个答案的目的,它本质上与我在问题中添加的纹理类非常相似。Texture类包含一个GetVertices方法,它返回一个多维的顶点浮点数组(我这样做是为了更好地可视化它们,更容易将纹理坐标添加到顶点,最终变成一个一维的浮点数组)。
这是我现在正在使用的代码的核心,但是它分布在多个类和方法等中。也可以被修改以适应其它目的。
如果这不是将线程标记为已解决的正确方法,或者这样做或代码有任何错误,请告诉我。我事先道歉,因为我不知道“如何堆栈溢出”。(我很少在这里提问或回答问题...)

相关问题