首页 > 其他分享 >在OpenGL中实现视角切换插值过渡动画

在OpenGL中实现视角切换插值过渡动画

时间:2024-11-10 13:29:43浏览次数:1  
标签:动画 1.0 glm OpenGL 插值 0.0 vec3 gl

在OpenGL中实现视角切换插值过渡动画可以通过以下步骤来完成:

一、定义视角结构体

首先,需要定义一个结构体来表示视角相关的信息,通常包括观察位置(Eye Position)、观察目标点(Look At Point)和上方向向量(Up Vector)。例如:

struct Camera {
    glm::vec3 eye;
    glm::vec3 center;
    glm::vec3 up;
};

这里假设使用了GLM库(OpenGL Mathematics Library)来处理向量和矩阵运算,glm::vec3 表示三维向量。

二、设置初始视角和目标视角

确定动画开始时的初始视角(initialCamera)和切换到的目标视角(targetCamera)。可以根据具体需求手动设置这些视角的值,比如:

Camera initialCamera = {
    glm::vec3(0.0f, 0.0f, 5.0f),  // 初始观察位置
    glm::vec3(0.0f, 0.0f, 0.0f),  // 初始观察目标点
    glm::vec3(0.0f, 1.0f, 0.0f)   // 初始上方向向量
};

Camera targetCamera = {
    glm::vec3(2.0f, 2.0f, 3.0f),  // 目标观察位置
    glm::vec3(0.0f, 0.0f, 0.0f),  // 目标观察目标点
    glm::vec3(0.0f, 1.0f, 0.0f)   // 目标上方向向量
};

三、选择插值方法

常用的插值方法有线性插值(Linear Interpolation,简称LERP)和球面线性插值(Spherical Linear Interpolation,简称SLERP)。

  1. 线性插值(LERP)
    • 线性插值是一种简单直接的插值方法。对于两个给定的值 ab,以及一个插值因子 t(取值范围在0到1之间),线性插值的公式为:result = a + t * (b - a)
    • 在视角切换的应用中,对于视角的每个分量(如观察位置、观察目标点、上方向向量)都可以分别使用线性插值来计算过渡值。例如,对于观察位置的插值计算如下:
glm::vec3 lerpEyePosition = initialCamera.eye + t * (targetCamera.eye - initialCamera.eye);

其中 t 是随着时间逐渐从0增加到1的插值因子,表示动画的进度。

  1. 球面线性插值(SLERP)
    • 球面线性插值主要用于在两个向量之间进行插值,并且能够保持向量的长度和方向的相对关系,使得插值结果更加自然。它的计算公式相对复杂一些,基于三角函数和向量点积等运算。
    • 对于视角的上方向向量等需要保持方向特性的情况,使用SLERP可能会得到更好的效果。例如,对于上方向向量的插值计算可以使用GLM库提供的 glm::slerp 函数(假设GLM库版本支持该功能):
glm::vec3 slerpUpVector = glm::slerp(initialCamera.up, targetCamera.up, t);

四、更新视角并渲染场景

在渲染循环中,根据选择的插值方法和当前的动画进度(由插值因子 t 表示)来更新视角信息,然后使用更新后的视角来渲染场景。

  1. 更新插值因子
    • 在渲染循环中,需要不断更新插值因子 t,使其随着时间逐渐从0增加到1。可以通过获取当前时间与动画总时长的比例来确定 t 的值。例如:
float totalAnimationTime = 2.0f;  // 设定动画总时长为2秒
float currentTime = static_cast<float>(glfwGetTime());  // 获取当前时间(假设使用GLFW库来获取时间)
float t = currentTime / totalAnimationTime;
if (t > 1.0f) t = 1.0f;  // 确保t不超过1
  1. 更新视角信息
    • 根据选择的插值方法,分别对视角的各个分量进行插值计算,得到更新后的视角结构体。例如,使用线性插值更新视角的示例如下:
Camera currentCamera;
currentCamera.eye = lerpEyePosition;
currentCamera.center = initialCamera.center;  // 观察目标点通常可以保持不变,也可根据需求进行插值计算
currentCamera.up = lerpUpVector;

或者使用球面线性插值更新视角的示例如下:

Camera currentCamera;
currentCamera.eye = lerpEyePosition;
currentCamera.center = initialCamera.center;
currentCamera.up = slerpUpVector;
  1. 渲染场景
    • 使用更新后的视角信息,通过设置OpenGL的观察矩阵等相关矩阵来渲染场景。例如,使用GLM库来设置观察矩阵的示例如下:
glm::mat4 viewMatrix = glm::lookAt(currentCamera.eye, currentCamera.center, currentCamera.up);
// 将viewMatrix应用到渲染管线中,具体应用方式取决于你的渲染框架和代码结构

通过不断重复上述步骤,在渲染循环中随着时间更新插值因子、更新视角信息并渲染场景,就可以实现OpenGL中视角切换的插值过渡动画。

请注意,上述代码示例只是一个简化的框架,在实际应用中,你可能需要根据具体的OpenGL渲染框架、使用的库以及应用场景等对代码进行进一步的完善和调整。

======================================================

以下是一个简单的 OpenGL 中实现视角切换插值过渡动画的 C++ 代码示例,使用了 GLFW 和 GLM 库:

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

// 定义相机结构体
struct Camera {
    glm::vec3 eye;
    glm::vec3 center;
    glm::vec3 up;
};

// 线性插值函数
glm::vec3 lerp(const glm::vec3& a, const glm::vec3& b, float t) {
    return a * (1.0f - t) + b * t;
}

int main() {
    // 初始化 GLFW
    if (!glfwInit()) {
        std::cerr << "GLFW initialization failed" << std::endl;
        return -1;
    }

    // 创建窗口
    GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL View Transition", nullptr, nullptr);
    if (!window) {
        std::cerr << "Window creation failed" << std::endl;
        glfwTerminate();
        return -1;
    }

    // 将窗口设置为当前上下文
    glfwMakeContextCurrent(window);

    // 初始化 GLEW
    if (glewInit()!= GLEW_OK) {
        std::cerr << "GLEW initialization failed" << std::endl;
        return -1;
    }

    // 设置初始视角和目标视角
    Camera initialCamera = {
        glm::vec3(0.0f, 0.0f, 5.0f),
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 1.0f, 0.0f)
    };

    Camera targetCamera = {
        glm::vec3(2.0f, 2.0f, 3.0f),
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 1.0f, 0.0f)
    };

    // 动画相关变量
    float totalAnimationTime = 2.0f; // 动画总时长
    float currentTime = 0.0f;
    float t = 0.0f;

    // 渲染循环
    while (!glfwWindowShouldClose(window)) {
        // 计算当前时间
        currentTime = static_cast<float>(glfwGetTime());
        // 计算插值因子
        t = currentTime / totalAnimationTime;
        if (t > 1.0f) t = 1.0f;

        // 计算当前视角
        Camera currentCamera;
        currentCamera.eye = lerp(initialCamera.eye, targetCamera.eye, t);
        currentCamera.center = lerp(initialCamera.center, targetCamera.center, t);
        currentCamera.up = lerp(initialCamera.up, targetCamera.up, t);

        // 设置视景体
        glm::mat4 viewMatrix = glm::lookAt(currentCamera.eye, currentCamera.center, currentCamera.up);
        glm::mat4 projectionMatrix = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);

        // 清除颜色缓冲和深度缓冲
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // 使用当前视角矩阵和投影矩阵进行渲染
        // 这里只是一个简单的示例,你需要根据自己的场景和渲染逻辑进行具体的渲染操作
        // 例如,如果你有一个物体要绘制,可以在这之前设置好模型矩阵,然后进行绘制

        // 交换缓冲区,显示渲染结果
        glfwSwapBuffers(window);

        // 处理输入事件
        glfwPollEvents();
    }

    // 释放资源
    glfwTerminate();
    return 0;
}

在上述代码中,首先定义了一个Camera结构体来表示相机的位置、目标点和上方向向量。然后,使用线性插值函数lerp来计算在初始视角和目标视角之间的过渡视角。在渲染循环中,根据当前时间计算插值因子t,并使用插值因子更新当前视角。最后,设置视景体并进行渲染操作。

请注意,这只是一个简单的示例,实际应用中你可能需要根据自己的场景和需求进行更多的设置和扩展,比如添加物体的绘制、处理输入事件等。同时,确保你已经正确地配置了 OpenGL 的环境和相关库的链接。

========================================================

以下是一个简单的 OpenGL 中实现视角切换插值过渡动画的 C++ 代码示例,使用了 GLFW 和 GLM 库:

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

// 定义相机结构体
struct Camera {
    glm::vec3 eye;
    glm::vec3 center;
    glm::vec3 up;
};

// 线性插值函数
glm::vec3 lerp(const glm::vec3& a, const glm::vec3& b, float t) {
    return a * (1.0f - t) + b * t;
}

int main() {
    // 初始化 GLFW
    if (!glfwInit()) {
        std::cerr << "GLFW initialization failed" << std::endl;
        return -1;
    }

    // 创建窗口
    GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL View Transition", nullptr, nullptr);
    if (!window) {
        std::cerr << "Window creation failed" << std::endl;
        glfwTerminate();
        return -1;
    }

    // 将窗口设置为当前上下文
    glfwMakeContextCurrent(window);

    // 初始化 GLEW
    if (glewInit()!= GLEW_OK) {
        std::cerr << "GLEW initialization failed" << std::endl;
        return -1;
    }

    // 设置初始视角和目标视角
    Camera initialCamera = {
        glm::vec3(0.0f, 0.0f, 5.0f),
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 1.0f, 0.0f)
    };

    Camera targetCamera = {
        glm::vec3(2.0f, 2.0f, 3.0f),
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 1.0f, 0.0f)
    };

    // 动画相关变量
    float totalAnimationTime = 2.0f; // 动画总时长
    float currentTime = 0.0f;
    float t = 0.0f;

    // 渲染循环
    while (!glfwWindowShouldClose(window)) {
        // 计算当前时间
        currentTime = static_cast<float>(glfwGetTime());
        // 计算插值因子
        t = currentTime / totalAnimationTime;
        if (t > 1.0f) t = 1.0f;

        // 计算当前视角
        Camera currentCamera;
        currentCamera.eye = lerp(initialCamera.eye, targetCamera.eye, t);
        currentCamera.center = lerp(initialCamera.center, targetCamera.center, t);
        currentCamera.up = lerp(initialCamera.up, targetCamera.up, t);

        // 设置视景体
        glm::mat4 viewMatrix = glm::lookAt(currentCamera.eye, currentCamera.center, currentCamera.up);
        glm::mat4 projectionMatrix = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);

        // 清除颜色缓冲和深度缓冲
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // 使用当前视角矩阵和投影矩阵进行渲染
        // 这里只是一个简单的示例,你需要根据自己的场景和渲染逻辑进行具体的渲染操作
        // 例如,如果你有一个物体要绘制,可以在这之前设置好模型矩阵,然后进行绘制

        // 交换缓冲区,显示渲染结果
        glfwSwapBuffers(window);

        // 处理输入事件
        glfwPollEvents();
    }

    // 释放资源
    glfwTerminate();
    return 0;
}

在上述代码中,首先定义了一个Camera结构体来表示相机的位置、目标点和上方向向量。然后,使用线性插值函数lerp来计算在初始视角和目标视角之间的过渡视角。在渲染循环中,根据当前时间计算插值因子t,并使用插值因子更新当前视角。最后,设置视景体并进行渲染操作。

请注意,这只是一个简单的示例,实际应用中你可能需要根据自己的场景和需求进行更多的设置和扩展,比如添加物体的绘制、处理输入事件等。同时,确保你已经正确地配置了 OpenGL 的环境和相关库的链接。

========================================================================

以下是使用不同语言实现的 OpenGL 视角切换插值过渡动画的代码示例:

Python (使用 PyOpenGL 库)

import glfw
import OpenGL.GL as gl
import numpy as np
from OpenGL.GL.shaders import compileShader, compileProgram
import glm

# 初始化 GLFW
if not glfw.init():
    raise Exception("GLFW initialization failed")

# 创建窗口
window = glfw.create_window(800, 600, "OpenGL Perspective Transition", None, None)
if not window:
    glfw.terminate()
    raise Exception("Window creation failed")

# 将窗口设为当前上下文
glfw.make_context_current(window)

# 初始化 GLEW
glewExperimental = True
if glewInit()!= GLEW_OK:
    raise Exception("GLEW initialization failed")

# 顶点着色器源码
vertex_shader_source = """
#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
"""

# 片段着色器源码
fragment_shader_source = """
#version 330 core
out vec4 FragColor;
void main()
{
    FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
"""

# 编译着色器
vertex_shader = compileShader(vertex_shader_source, gl.GL_VERTEX_SHADER)
fragment_shader = compileShader(fragment_shader_source, gl.GL_FRAGMENT_SHADER)
shader_program = compileProgram(vertex_shader, fragment_shader)

# 设置顶点数据
vertices = np.array([
    -0.5, -0.5, 0.0,
     0.5, -0.5, 0.0,
     0.0,  0.5, 0.0
], dtype=np.float32)

# 创建顶点缓冲区对象(VBO)和顶点数组对象(VAO)
vbo = gl.glGenBuffers(1)
vao = gl.glGenVertexArrays(1)

gl.glBindVertexArray(vao)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbo)
gl.glBufferData(gl.GL_ARRAY_BUFFER, vertices.nbytes, vertices, gl.GL_STATIC_DRAW)

gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 3 * 4, None)
gl.glEnableVertexAttribArray(0)

gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
gl.glBindVertexArray(0)

# 定义初始视角和目标视角
initial_eye = glm.vec3(0.0, 0.0, 3.0)
target_eye = glm.vec3(2.0, 2.0, 5.0)
initial_target = glm.vec3(0.0, 0.0, 0.0)
target_target = glm.vec3(0.0, 0.0, 1.0)
initial_up = glm.vec3(0.0, 1.0, 0.0)
target_up = glm.vec3(0.0, 1.0, 0.5)

# 动画时长和时间变量
animation_duration = 3.0  # 秒
current_time = 0.0

def update_view_matrix():
    global current_time
    if current_time < animation_duration:
        # 计算插值因子
        t = current_time / animation_duration
        # 插值计算观察位置、目标点和上方向向量
        eye = glm.lerp(initial_eye, target_eye, t)
        target = glm.lerp(initial_target, target_target, t)
        up = glm.slerp(initial_up, target_up, t)
    else:
        eye = target_eye
        target = target_target
        up = target_up

    # 构建观察矩阵
    view_matrix = glm.lookAt(eye, target, up)
    return view_matrix

def render():
    global current_time
    while not glfw.window_should_close(window):
        # 处理输入
        process_input(window)

        # 清除颜色缓冲和深度缓冲
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)

        # 使用着色器程序
        gl.glUseProgram(shader_program)

        # 更新观察矩阵
        view_matrix = update_view_matrix()
        # 获取 uniform 变量的位置
        view_location = gl.glGetUniformLocation(shader_program, "view")
        projection_location = gl.glGetUniformLocation(shader_program, "projection")
        model_location = gl.glGetUniformLocation(shader_program, "model")

        # 传递矩阵到着色器
        gl.glUniformMatrix4fv(view_location, 1, gl.GL_FALSE, glm.value_ptr(view_matrix))
        projection_matrix = glm.perspective(glm.radians(45.0), 800 / 600, 0.1, 100.0)
        gl.glUniformMatrix4fv(projection_location, 1, gl.GL_FALSE, glm.value_ptr(projection_matrix))
        model_matrix = glm.mat4(1.0)
        gl.glUniformMatrix4fv(model_location, 1, gl.GL_FALSE, glm.value_ptr(model_matrix))

        # 绘制三角形
        gl.glBindVertexArray(vao)
        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3)
        gl.glBindVertexArray(0)

        # 交换缓冲区,显示渲染结果
        glfw.swap_buffers(window)

        # 更新时间
        current_time += glfw.get_time() - current_time
        glfw.poll_events()

    glfw.terminate()

def process_input(window):
    if glfw.get_key(window, glfw.KEY_ESCAPE) == glfw.PRESS:
        glfw.set_window_should_close(window, True)

if __name__ == "__main__":
    render()

以下是使用不同语言实现的 OpenGL 视角切换插值过渡动画的代码示例:

  • Java(使用 LWJGL 库)
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.system.MemoryStack;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.NULL;

public class OpenGLViewTransition {
    // 窗口的宽度和高度
    private static final int WIDTH = 800;
    private static final int HEIGHT = 600;

    // 初始视角和目标视角
    private static float[] initialEye = {0.0f, 0.0f, 3.0f};
    private static float[] targetEye = {2.0f, 2.0f, 5.0f};
    // 初始和目标观察目标点
    private static float[] initialCenter = {0.0f, 0.0f, 0.0f};
    private static float[] targetCenter = {1.0f, 1.0f, 0.0f};
    // 初始和目标上方向向量
    private static float[] initialUp = {0.0f, 1.0f, 0.0f};
    private static float[] targetUp = {0.5f, 0.5f, 1.0f};

    // 插值因子
    private static float t = 0.0f;
    // 动画是否完成的标志
    private static boolean animationFinished = false;

    public static void main(String[] args) {
        // 设置错误回调
        GLFWErrorCallback.createPrint(System.err).set();

        // 初始化 GLFW
        if (!glfwInit()) {
            throw new IllegalStateException("GLFW initialization failed");
        }

        // 创建窗口
        long window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL View Transition", NULL, NULL);
        if (window == NULL) {
            throw new RuntimeException("Failed to create the GLFW window");
        }

        // 设置窗口的当前上下文
        glfwMakeContextCurrent(window);
        // 启用垂直同步
        glfwSwapInterval(1);

        // 初始化 GLEW
        GL.createCapabilities();

        // 设置视口
        glViewport(0, 0, WIDTH, HEIGHT);

        // 主循环
        while (!glfwWindowShouldClose(window)) {
            // 处理输入
            processInput(window);

            // 如果动画未完成,进行插值计算并更新视角
            if (!animationFinished) {
                t += 0.01f; // 增加插值因子
                if (t >= 1.0f) {
                    t = 1.0f;
                    animationFinished = true;
                }

                // 计算插值后的观察位置
                float[] interpolatedEye = new float[3];
                for (int i = 0; i < 3; i++) {
                    interpolatedEye[i] = initialEye[i] + t * (targetEye[i] - initialEye[i]);
                }

                // 计算插值后的观察目标点
                float[] interpolatedCenter = new float[3];
                for (int i = 0; i < 3; i++) {
                    interpolatedCenter[i] = initialCenter[i] + t * (targetCenter[i] - initialCenter[i]);
                }

                // 计算插值后的上方向向量
                float[] interpolatedUp = new float[3];
                for (int i = 0; i < 3; i++) {
                    interpolatedUp[i] = initialUp[i] + t * (targetUp[i] - initialUp[i]);
                }

                // 设置视角
                setView(interpolatedEye, interpolatedCenter, interpolatedUp);
            }

            // 清除颜色缓冲
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            // 绘制场景(这里只是一个简单的三角形示例)
            drawScene();

            // 交换缓冲区,显示渲染结果
            glfwSwapBuffers(window);

            // 检查并调用事件回调
            glfwPollEvents();
        }

        // 释放资源
        glfwFreeCallbacks(window);
        glfwDestroyWindow(window);

        // 终止 GLFW
        glfwTerminate();
    }

    private static void processInput(long window) {
        if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
            glfwSetWindowShouldClose(window, true);
        }
    }

    private static void setView(float[] eye, float[] center, float[] up) {
        try (MemoryStack stack = stackPush()) {
            FloatBuffer eyeBuffer = BufferUtils.createFloatBuffer(eye.length);
            eyeBuffer.put(eye).flip();
            FloatBuffer centerBuffer = BufferUtils.createFloatBuffer(center.length);
            centerBuffer.put(center).flip();
            FloatBuffer upBuffer = BufferUtils.createFloatBuffer(up.length);
            upBuffer.put(up).flip();

            // 设置观察矩阵
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            gluLookAt(eyeBuffer.get(0), eyeBuffer.get(1), eyeBuffer.get(2),
                    centerBuffer.get(0), centerBuffer.get(1), centerBuffer.get(2),
                    upBuffer.get(0), upBuffer.get(1), upBuffer.get(2));
        }
    }

    private static void drawScene() {
        glBegin(GL_TRIANGLES);
        glColor3f(1.0f, 0.0f, 0.0f);
        glVertex3f(-0.5f, -0.5f, 0.0f);
        glColor3f(0.0f, 1.0f, 0.0f);
        glVertex3f(0.5f, -0.5f, 0.0f);
        glColor3f(0.0f, 0.0f, 1.0f);
        glVertex3f(0.0f, 0.5f, 0.0f);
        glEnd();
    }
}
  • Python(使用 PyOpenGL 和 GLFW 库)
import glfw
import OpenGL.GL as gl
import numpy as np
from OpenGL.GL.shaders import compileProgram, compileShader
import glm

# 窗口的宽度和高度
WIDTH = 800
HEIGHT = 600

# 初始视角和目标视角
initial_eye = glm.vec3(0.0, 0.0, 3.0)
target_eye = glm.vec3(2.0, 2.0, 5.0)
# 初始和目标观察目标点
initial_center = glm.vec3(0.0, 0.0, 0.0)
target_center = glm.vec3(1.0, 1.0, 0.0)
# 初始和目标上方向向量
initial_up = glm.vec3(0.0, 1.0, 0.0)
target_up = glm.vec3(0.5, 0.5, 1.0)

# 插值因子
t = 0.0
# 动画是否完成的标志
animation_finished = False

def main():
    # 初始化 GLFW
    if not glfw.init():
        return

    # 创建窗口
    window = glfw.create_window(WIDTH, HEIGHT, "OpenGL View Transition", None, None)
    if not window:
        glfw.terminate()
        return

    # 设置窗口的当前上下文
    glfw.make_context_current(window)

    # 设置视口
    gl.glViewport(0, 0, WIDTH, HEIGHT)

    # 编译顶点着色器和片段着色器
    vertex_shader_source = """
    #version 330
    layout (location = 0) in vec3 position;
    uniform mat4 view;
    uniform mat4 projection;
    void main()
    {
        gl_Position = projection * view * vec4(position, 1.0);
    }
    """
    fragment_shader_source = """
    #version 330
    out vec4 fragColor;
    void main()
    {
        fragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
    """
    shader_program = compileProgram(
        compileShader(vertex_shader_source, gl.GL_VERTEX_SHADER),
        compileShader(fragment_shader_source, gl.GL_FRAGMENT_SHADER)
    )

    # 设置三角形的顶点数据
    vertices = np.array([
        -0.5, -0.5, 0.0,
        0.5, -0.5, 0.0,
        0.0, 0.5, 0.0
    ], dtype=np.float32)

    # 创建顶点缓冲对象(VBO)和顶点数组对象(VAO)
    vbo = gl.glGenBuffers(1)
    vao = gl.glGenVertexArrays(1)
    gl.glBindVertexArray(vao)
    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbo)
    gl.glBufferData(gl.GL_ARRAY_BUFFER, vertices.nbytes, vertices, gl.GL_STATIC_DRAW)
    gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
    gl.glEnableVertexAttribArray(0)

    # 主循环
    while not glfw.window_should_close(window):
        # 处理输入
        process_input(window)

        # 如果动画未完成,进行插值计算并更新视角
        if not animation_finished:
            t += 0.01
            if t >= 1.0:
                t = 1.0
                animation_finished = True

            # 计算插值后的观察位置
            interpolated_eye = glm.vec3(
                initial_eye.x + t * (target_eye.x - initial_eye.x),
                initial_eye.y + t * (target_eye.y - initial_eye.y),
                initial_eye.z + t * (target_eye.z - initial_eye.z)
            )

            # 计算插值后的观察目标点
            interpolated_center = glm.vec3(
                initial_center.x + t * (target_center.x - initial_center.x),
                initial_center.y + t * (target_center.y - initial_center.y),
                initial_center.z + t * (target_center.z - initial_center.z)
            )

            # 计算插值后的上方向向量
            interpolated_up = glm.vec3(
                initial_up.x + t * (target_up.x - initial_up.x),
                initial_up.y + t * (target_up.y - initial_up.y),
                initial_up.z + t * (target_up.z - initial_up.z)
            )

            # 计算观察矩阵和投影矩阵
            view_matrix = glm.lookAt(interpolated_eye, interpolated_center, interpolated_up)
            projection_matrix = glm.perspective(glm.radians(45.0), WIDTH / HEIGHT, 0.1, 100.0)

            # 使用 shader_program
            gl.glUseProgram(shader_program)
            # 上传观察矩阵和投影矩阵到 shader
            view_location = gl.glGetUniformLocation(shader_program, "view")
            projection_location = gl.glGetUniformLocation(shader_program, "projection")
            gl.glUniformMatrix4fv(view_location, 1, gl.GL_FALSE, glm.value_ptr(view_matrix))
            gl.glUniformMatrix4fv(projection_location, 1, gl.GL_FALSE, glm.value_ptr(projection_matrix))

        # 清除颜色缓冲和深度缓冲
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)

        # 绘制三角形
        gl.glBindVertexArray(vao)
        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3)

        # 交换缓冲区,显示渲染结果
        glfw.swap_buffers(window)

        # 检查并调用事件回调
        glfw.poll_events()

    # 释放资源
    gl.glDeleteVertexArrays(1, [vao])
    gl.glDeleteBuffers(1, [vbo])
    gl.glDeleteProgram(shader_program)

    # 终止 GLFW
    glfw.terminate()

def process_input(window):
    if glfw.get_key(window, glfw.KEY_ESCAPE) == glfw.PRESS:
        glfw.set_window_should_close(window, True)

if __name__ == "__main__":
    main()

以下是一个简单的 WebGL 视角切换插值过渡动画的代码示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>WebGL View Transition</title>
</head>

<body>
    <canvas id="glCanvas" width="500" height="500"></canvas>
    <script type="text/javascript">
        // 获取 canvas 元素和 WebGL 上下文
        const canvas = document.getElementById('glCanvas');
        const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');

        // 顶点着色器代码
        const vertexShaderSource = `
            attribute vec3 aPosition;
            uniform mat4 uProjectionMatrix;
            uniform mat4 uModelViewMatrix;
            void main() {
                gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
            }
        `;

        // 片段着色器代码
        const fragmentShaderSource = `
            precision mediump float;
            void main() {
                gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
            }
        `;

        // 创建着色器程序
        function createShaderProgram(gl, vertexShaderSource, fragmentShaderSource) {
            const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
            const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);

            const shaderProgram = gl.createProgram();
            gl.attachShader(shaderProgram, vertexShader);
            gl.attachShader(shaderProgram, fragmentShader);
            gl.linkProgram(shaderProgram);

            if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
                console.error('Error linking shader program:', gl.getProgramInfoLog(shaderProgram));
                gl.deleteProgram(shaderProgram);
                return null;
            }

            return shaderProgram;
        }

        // 创建着色器
        function createShader(gl, type, source) {
            const shader = gl.createShader(type);
            gl.shaderSource(shader, source);
            gl.compileShader(shader);

            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                console.error('Error compiling shader:', gl.getShaderInfoLog(shader));
                gl.deleteShader(shader);
                return null;
            }

            return shader;
        }

        // 初始化 WebGL
        function initWebGL() {
            const shaderProgram = createShaderProgram(gl, vertexShaderSource, fragmentShaderSource);

            if (!shaderProgram) {
                console.error('Failed to create shader program');
                return;
            }

            // 设置顶点数据
            const vertices = new Float32Array([
                -0.5, -0.5, 0.0,
                0.5, -0.5, 0.0,
                0.0, 0.5, 0.0
            ]);

            const buffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
            gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

            const aPosition = gl.getAttribLocation(shaderProgram, 'aPosition');
            gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
            gl.enableVertexAttribArray(aPosition);

            return shaderProgram;
        }

        // 计算插值
        function interpolate(current, target, t) {
            return [
                current[0] + (target[0] - current[0]) * t,
                current[1] + (target[1] - current[1]) * t,
                current[2] + (target[2] - current[2]) * t
            ];
        }

        let currentView = [0, 0, 5]; // 当前视角位置
        let targetView = [2, 2, 10]; // 目标视角位置
        let t = 0; // 插值参数
        let shaderProgram;

        function drawScene() {
            if (!shaderProgram) {
                shaderProgram = initWebGL();
            }

            // 更新插值参数
            t += 0.01;
            if (t >= 1) {
                t = 1;
            }

            // 计算当前视角位置的插值
            const interpolatedView = interpolate(currentView, targetView, t);

            // 创建投影矩阵和模型视图矩阵
            const projectionMatrix = mat4.create();
            mat4.perspective(projectionMatrix, Math.PI / 4, canvas.width / canvas.height, 0.1, 100.0);

            const modelViewMatrix = mat4.create();
            mat4.lookAt(modelViewMatrix, interpolatedView, [0, 0, 0], [0, 1, 0]);

            // 将矩阵传递给着色器
            const uProjectionMatrix = gl.getUniformLocation(shaderProgram, 'uProjectionMatrix');
            const uModelViewMatrix = gl.getUniformLocation(shaderProgram, 'uModelViewMatrix');
            gl.uniformMatrix4fv(uProjectionMatrix, false, projectionMatrix);
            gl.uniformMatrix4fv(uModelViewMatrix, false, modelViewMatrix);

            // 清除画布并绘制三角形
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            gl.drawArrays(gl.TRIANGLES, 0, 3);

            // 如果插值未完成,继续请求动画帧
            if (t < 1) {
                requestAnimationFrame(drawScene);
            }
        }

        // 开始动画
        requestAnimationFrame(drawScene);
    </script>
</body>

</html>

在上述代码中:

  1. 首先获取了 canvas 元素和 WebGL 上下文。
  2. 定义了顶点着色器和片段着色器的代码,顶点着色器负责计算顶点的位置,片段着色器负责设置像素的颜色。
  3. 创建了着色器程序,并初始化了顶点数据。
  4. interpolate 函数用于计算两个视角位置之间的插值。
  5. drawScene 函数中,不断更新插值参数 t,计算当前视角位置的插值,并创建投影矩阵和模型视图矩阵。然后将矩阵传递给着色器,清除画布并绘制三角形。如果插值未完成,继续请求动画帧以实现动画效果。

请注意,上述代码仅是一个简单的示例,实际应用中可能需要根据具体需求进行更多的优化和扩展。同时,确保浏览器支持 WebGL 才能正常运行该代码。

标签:动画,1.0,glm,OpenGL,插值,0.0,vec3,gl
From: https://www.cnblogs.com/DesertCactus/p/18537886

相关文章

  • AnimateDiff:一款强大的制作丝滑动画视频插件,轻松让你的图片动起来
    得益于StableDiffusion的开源,目前很多开发者推出了基于SD生成动画和视频的应用技术和扩展插件,在众多的技术中,AnimateDiff插件以“效果丝滑、稳定、无闪烁”等特性,成为目前Stablediffusion中效果最好的生成动画视频插件之一。今天就给大家详细介绍一下在Stablediffusion中......
  • 【VBA实战】用Excel制作排序算法动画续
    为什么会产生用excel来制作排序算法动画的念头,参见【VBA实战】用Excel制作排序算法动画一文。这篇文章贴出我所制作的所有排序算法动画效果和源码,供大家参考。冒泡排序:插入排序:选择排序:快速排序:归并排序:堆排序:希尔排序:完整源码如下。OptionExplicitPublichm......
  • OpenGL 纹理采样 在GPU中哪个部件完成
    OpenGL纹理采样主要在GPU的流式多处理器(StreamingMultiprocessor,SM)中完成。SM内部包含多个用于执行计算的核心(Core)以及纹理缓存(TextureCache)等部件,这些部件协同工作来实现纹理采样。具体过程如下:纹理数据获取:当需要进行纹理采样时,首先会从纹理内存(通常是显存中的一块区......
  • CSS3中动画的使用animation
    1.基本使用2.其他属性3.复合属性......
  • 用GLSL 和 GLM 实现OpenGL trackball
    以下是一个使用GLSL(OpenGLShadingLanguage)和GLM(OpenGLMathematics)库来实现OpenGLtrackball功能的示例。这个示例将展示如何通过鼠标操作来旋转场景中的物体,就像在操作一个虚拟的轨迹球一样。1.准备工作包含必要的头文件和库:需要包含GLFW头文件用于创建窗口和处理事件,GLA......
  • OpenGL 如何实现 trackball
    在OpenGL中实现trackball(轨迹球)功能可以让用户通过鼠标操作来旋转场景中的物体,就好像在操作一个虚拟的轨迹球一样。以下是一种常见的实现方式的步骤:基本原理Trackball的基本思想是将二维的鼠标移动映射到三维空间中的旋转操作。当用户在屏幕上按下鼠标并移动时,根据鼠标的起......
  • devc++配置opengl库
    由于VisualStudio太占内存,所以用老古董devc++配图形学的环境。用到的文件下载链接Step1:建项目首先打开dev点文件--新建--项目--Multimedia--OpenGLc++/c都行(我这里用的c++)名称最好用英文,然后确定,保存的地方也最好没有中文路径Step2:添加库文件找到DEV-C++的安装目录(右键......
  • 数值分析作业(第五章):代码+手写计算:插值算法 - Lagrange插值、Newton插值、Hermite插值
    《数值计算方法》丁丽娟-数值实验作业-第五章(MATLAB)作业P171:1,3,6,7,8,15,16数值实验P175:1代码+手写计算:插值算法-Lagrange插值、Newton插值、Hermite插值、分段插值推荐网课:数值分析-东南大学-bilibili数值实验作业(第五章)代码仓库:https://github.com/sylvandi......
  • Vue+CSS 炫酷新年特效教程:极光背景+彩带+粒子动画 用 Vue 和 CSS 动画打造 2025 新年
    效果图......
  • WPF 抖动动画
    .NETWPF抖动动画1Demo2Code<!--水平抖动--><ButtonContent="HelloWorld"><Button.RenderTransform><TranslateTransformx:Name="translateTransform"/></Button.RenderTransform><Butto......