当前位置: 首页 > news >正文

JOGL 从入门到精通:开启 Java 3D 图形编程之旅

一、引言

Java 作为一门广泛应用的编程语言,在图形编程领域也有着强大的工具和库。JOGL(Java OpenGL)便是其中之一,它为 Java 开发者提供了访问 OpenGL(Open Graphics Library)功能的接口,使得在 Java 平台上创建高性能、交互式的 2D 和 3D 图形应用程序成为可能。从简单的图形绘制到复杂的 3D 游戏开发、科学可视化等领域,JOGL 都展现出了其独特的优势和广泛的应用前景。本文将带你逐步深入学习 JOGL,从基础的环境搭建到高级的图形渲染技巧,助你掌握这一强大的图形编程工具。

二、JOGL 基础概述

(一)什么是 JOGL

JOGL 是一个 Java 绑定的 OpenGL 库,它允许 Java 程序利用 OpenGL 的强大图形渲染能力。OpenGL 是一个跨平台的图形 API,提供了丰富的函数和方法来创建、操作和渲染 2D 和 3D 图形。JOGL 通过 Java 本地接口(JNI)将这些功能封装起来,使得 Java 开发者可以使用熟悉的 Java 语法来调用 OpenGL 的函数,从而在 Java 应用程序中实现高性能的图形处理。

(二)JOGL 的特点

  • 高性能:由于基于 OpenGL,JOGL 能够充分利用显卡的硬件加速功能,实现高效的图形渲染,对于复杂的 3D 场景和大量图形元素的处理表现出色。
  • 跨平台性:与 Java 语言的特性一致,JOGL 应用程序可以在不同的操作系统上运行,如 Windows、Linux、macOS 等,只要系统安装了相应的 OpenGL 驱动程序,就能够保证图形程序的正常执行,大大提高了代码的可移植性和应用范围。
  • 丰富的功能:支持 2D 和 3D 图形的绘制,包括基本图形(如点、线、三角形等)、复杂多边形、纹理映射、光照效果、几何变换(平移、旋转、缩放)、视图控制、模型加载等一系列图形处理功能,能够满足从简单图形界面到复杂 3D 游戏和专业图形应用的开发需求。

三、环境搭建

(一)安装 Java 开发环境

首先,确保已经安装了最新版本的 Java 开发工具包(JDK)。可以从 Oracle 官方网站或 OpenJDK 项目网站下载适合操作系统的 JDK 版本,并按照安装向导进行安装。安装完成后,通过在命令行中输入 java -version 命令来验证 Java 是否正确安装以及查看安装的版本信息。

(二)下载和配置 JOGL

  1. 从 JOGL 的官方网站(JOGL - Java Binding for the OpenGL API)下载 JOGL 的二进制发布包。通常会提供针对不同操作系统和 Java 版本的预编译库文件。
  2. 解压下载的文件,将 JOGL 的库文件(.jar 文件)添加到项目的类路径中。在使用集成开发环境(IDE)如 Eclipse 或 IntelliJ IDEA 时,可以通过项目设置中的 “Libraries” 或 “Dependencies” 选项来添加这些库文件。同时,还需要将 JOGL 依赖的本地库文件(.dll 文件 for Windows,.so 文件 for Linux,.dylib 文件 for macOS)所在的目录添加到系统的库路径中,这一步骤可能因操作系统和 IDE 的不同而有所差异,一般可以通过修改环境变量(如 PATH 或 LD_LIBRARY_PATH)来实现。

(三)创建第一个 JOGL 项目

在 IDE 中创建一个新的 Java 项目,然后创建一个简单的 Java 类作为入口点。在类中导入 JOGL 的相关包,例如:

import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLCanvas;
import javax.swing.JFrame;

这些包涵盖了 JOGL 中用于初始化 OpenGL 上下文、处理图形绘制事件以及创建显示窗口等基本功能的类和接口。接下来,就可以开始编写代码来创建一个简单的 JOGL 窗口并进行基本的图形绘制操作。

四、JOGL 图形绘制基础

(一)创建 OpenGL 上下文和窗口

public class FirstJOGLApp {public static void main(String[] args) {// 设置 OpenGL 能力GLCapabilities capabilities = new GLCapabilities(GLProfile.get(GLProfile.GL2));// 创建 GLCanvasGLCanvas canvas = new GLCanvas(capabilities);// 创建 JFrame 窗口JFrame frame = new JFrame("First JOGL Application");frame.getContentPane().add(canvas);frame.setSize(640, 480);frame.setVisible(true);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}
}

在上述代码中,首先通过 GLCapabilities 类设置所需的 OpenGL 版本(这里选择 GL2),然后创建 GLCanvas 对象作为 OpenGL 绘图的画布,并将其添加到 JFrame 窗口中。最后设置窗口的大小、可见性和关闭操作,这样就创建了一个基本的 JOGL 应用程序窗口,尽管此时窗口中还没有绘制任何图形。

(二)绘制基本图形

为了在窗口中绘制图形,需要实现 GLEventListener 接口,该接口定义了一系列用于处理 OpenGL 绘图事件的方法,其中关键的是 display 方法,在这个方法中进行图形绘制操作。

public class SimpleShapeDrawer implements GLEventListener {@Overridepublic void init(GLAutoDrawable drawable) {// 初始化操作,这里可以设置一些 OpenGL 的初始状态GL2 gl = drawable.getGL().getGL2();gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 设置背景颜色为黑色}@Overridepublic void display(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();gl.glClear(GL.GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区gl.glBegin(GL2.GL_TRIANGLES); // 开始绘制三角形gl.glColor3f(1.0f, 0.0f, 0.0f); // 设置当前颜色为红色gl.glVertex2f(-0.5f, -0.5f); // 三角形的第一个顶点gl.glColor3f(0.0f, 1.0f, 0.0f); // 设置当前颜色为绿色gl.glVertex2f(0.5f, -0.5f); // 三角形的第二个顶点gl.glColor3f(0.0f, 0.0f, 1.0f); // 设置当前颜色为蓝色gl.glVertex2f(0.0f, 0.5f); // 三角形的第三个顶点gl.glEnd(); // 结束绘制三角形}@Overridepublic void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {// 处理窗口大小改变事件}@Overridepublic void dispose(GLAutoDrawable drawable) {// 资源释放操作}
}

在 display 方法中,首先使用 glClear 方法清除颜色缓冲区,然后使用 glBegin 和 glEnd 方法定义了一个三角形的绘制过程,通过 glColor3f 方法设置每个顶点的颜色,glVertex2f 方法指定顶点的坐标,这样就在窗口中绘制了一个彩色的三角形。

(三)理解 OpenGL 坐标系统和绘图原理

OpenGL 使用一个右手坐标系,在 2D 情况下,原点通常位于窗口的左下角,x 轴向右为正方向,y 轴向上为正方向。在 3D 情况下,增加了 z 轴,指向屏幕外为正方向。当调用 glVertex 函数时,就是在这个坐标系中指定图形的顶点位置。而 glBegin 和 glEnd 之间的一系列顶点定义了一个基本图形(如三角形、四边形等),OpenGL 根据这些顶点信息进行图形的渲染。不同的基本图形绘制模式(如 GL_TRIANGLESGL_QUADS 等)决定了如何将这些顶点组合成最终的图形,理解这些坐标系统和绘图原理是进行复杂图形绘制和 3D 建模的基础。

五、JOGL 图形渲染进阶

(一)纹理映射

纹理映射是将 2D 图像(纹理)应用到 3D 模型表面的技术,使得模型更加逼真和生动。

public class TextureMappingExample implements GLEventListener {private int textureId;@Overridepublic void init(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();try {// 加载纹理图像BufferedImage image = ImageIO.read(new File("texture.jpg"));// 生成纹理对象textureId = createTexture(gl, image);} catch (IOException e) {e.printStackTrace();}}private int createTexture(GL2 gl, BufferedImage image) {int[] textureIds = new int[1];gl.glGenTextures(1, textureIds, 0);int textureId = textureIds[0];gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId);gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR);gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);gl.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGB, image.getWidth(), image.getHeight(),0, GL2.GL_RGB, GL2.GL_UNSIGNED_BYTE, new DataBufferByte(image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth()), image.getWidth() * image.getHeight() * 3));return textureId;}@Overridepublic void display(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);gl.glEnable(GL2.GL_TEXTURE_2D);gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId);gl.glBegin(GL2.GL_QUADS);gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-0.5f, -0.5f, 0.0f);gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(0.5f, -0.5f, 0.0f);gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(0.5f, 0.5f, 0.0f);gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-0.5f, 0.5f, 0.0f);gl.glEnd();gl.glDisable(GL2.GL_TEXTURE_2D);}// 其他方法的实现...
}

在上述代码中,init 方法用于加载纹理图像并生成纹理对象,通过 glGenTextures 生成纹理 ID,然后使用 glTexParameteri 设置纹理过滤参数,最后使用 glTexImage2D 将图像数据上传到纹理对象中。在 display 方法中,首先启用纹理,绑定纹理对象,然后在绘制四边形时,通过 glTexCoord2f 方法指定每个顶点对应的纹理坐标,这样就将纹理正确地映射到了四边形表面。

(二)光照效果

光照效果可以增强 3D 场景的真实感,JOGL 支持多种光照模型,如环境光、漫反射光、镜面反射光等。

public class LightingExample implements GLEventListener {@Overridepublic void init(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();gl.glEnable(GL2.GL_LIGHTING);gl.glEnable(GL2.GL_LIGHT0);// 设置环境光float[] ambientLight = { 0.2f, 0.2f, 0.2f, 1.0f };gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_AMBIENT, ambientLight, 0);// 设置漫反射光float[] diffuseLight = { 0.8f, 0.8f, 0.8f, 1.0f };gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_DIFFUSE, diffuseLight, 0);// 设置光源位置float[] lightPosition = { 0.0f, 0.0f, 2.0f, 1.0f };gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, lightPosition, 0);gl.glEnable(GL2.GL_COLOR_MATERIAL);}@Overridepublic void display(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);gl.glPushMatrix();gl.glTranslatef(0.0f, 0.0f, -2.0f);// 绘制一个简单的 3D 物体,如球体glut.glutSolidSphere(0.5f, 32, 32);gl.glPopMatrix();}// 其他方法的实现...
}

在 init 方法中,首先启用光照和一个光源(GL_LIGHT0),然后分别设置环境光、漫反射光的颜色和光源的位置,并启用颜色材质,使得物体的颜色能够受到光照的影响。在 display 方法中,绘制一个球体,并通过 glTranslatef 方法将其移到合适的位置,在光照的作用下,球体将呈现出更加真实的光影效果。

(三)几何变换

几何变换包括平移、旋转和缩放,通过这些变换可以实现 3D 模型的动态效果和场景的构建。

public class TransformationExample implements GLEventListener {private float angle = 0.0f;@Overridepublic void display(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);gl.glLoadIdentity();gl.glTranslatef(0.0f, 0.0f, -3.0f);gl.glRotatef(angle, 0.0f, 1.0f, 0.0f);// 绘制一个立方体drawCube(gl);angle += 0.5f;}private void drawCube(GL2 gl) {gl.glBegin(GL2.GL_QUADS);// 前面gl.glColor3f(1.0f, 0.0f, 0.0f);gl.glVertex3f(-0.5f, -0.5f, 0.5f);gl.glVertex3f(0.5f, -0.5f, 0.5f);gl.glVertex3f(0.5f, 0.5f, 0.5f);gl.glVertex3f(-0.5f, 0.5f, 0.5f);// 后面gl.glColor3f(0.0f, 1.0f, 0.0f);gl.glVertex3f(-0.5f, -0.5f, -0.5f);gl.glVertex3f(0.5f, -0.5f, -0.5f);gl.glVertex3f(0.5f, 0.5f, -0.5f);gl.glVertex3f(-0.5f, 0.5f, -0.5f);// 其他面的绘制...gl.glEnd();}// 其他方法的实现...
}

在 display 方法中,首先使用 glLoadIdentity 重置当前矩阵为单位矩阵,然后通过 glTranslatef 将坐标系原点移动到合适的位置,接着使用 glRotatef 方法根据不断变化的角度绕 y 轴旋转,在每次绘制立方体时,立方体都会绕 y 轴旋转一定角度,从而实现动态的旋转效果。通过类似的方法,还可以实现平移和缩放变换,以及组合多种变换来创建复杂的动画效果和场景布局。

六、JOGL 高级应用

(一)3D 模型加载与渲染

JOGL 可以与一些 3D 模型加载库(如 Assimp 等)结合使用,来加载复杂的 3D 模型并进行渲染。以下是一个使用 Assimp 加载 3D 模型并在 JOGL 中渲染的简单示例:

import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLCanvas;
import javax.swing.JFrame;
import org.lwjgl.assimp.AIAnimation;
import org.lwjgl.assimp.AIColor4D;
import org.lwjgl.assimp.AIFace;
import org.lwjgl.assimp.AIMesh;
import org.lwjgl.assimp.AINode;
import org.lwjgl.assimp.AIScene;
import org.lwjgl.assimp.AIVector3D;
import org.lwjgl.assimp.Assimp;import java.nio.FloatBuffer;
import java.nio.IntBuffer;import static org.lwjgl.assimp.Assimp.aiGetMeshBoundingBox;
import static org.lwjgl.assimp.Assimp.aiProcess_Triangulate;
import static org.lwjgl.assimp.Assimp.aiProcess_GenSmoothNormals;
import static org.lwjgl.assimp.Assimp.aiProcess_FlipUVs;
import static org.lwjgl.assimp.Assimp.aiProcess_CalcTangentSpace;
import static org.lwjgl.system.MemoryUtil.memAllocFloat;
import static org.lwjgl.system.MemoryUtil.memAllocInt;
import static org.lwjgl.system.MemoryUtil.memFree;public class ModelLoadingExample implements GLEventListener {private int[] vaoId = new int[1];private int[] vboId = new int[3];private AIScene scene;public ModelLoadingExample() {// 加载 3D 模型,这里以一个简单的 cube.obj 为例scene = Assimp.aiImportFile("cube.obj", aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);}@Overridepublic void init(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();// 生成并绑定 VAOgl.glGenVertexArrays(1, vaoId, 0);gl.glBindVertexArray(vaoId[0]);// 处理模型的每个网格for (int i = 0; i < scene.mNumMeshes(); i++) {AIMesh mesh = AIMesh.create(scene.mMeshes().get(i));// 顶点坐标数据FloatBuffer vertices = memAllocFloat(mesh.mNumVertices() * 3);for (int j = 0; j < mesh.mNumVertices(); j++) {AIVector3D vertex = mesh.mVertices().get(j);vertices.put(vertex.x()).put(vertex.y()).put(vertex.z());}vertices.flip();// 顶点法向量数据FloatBuffer normals = memAllocFloat(mesh.mNumVertices() * 3);for (int j = 0; j < mesh.mNumVertices(); j++) {AIVector3D normal = mesh.mNormals().get(j);normals.put(normal.x()).put(normal.y()).put(normal.z());}normals.flip();// 面索引数据IntBuffer indices = memAllocInt(mesh.mNumFaces() * 3);for (int j = 0; j < mesh.mNumFaces(); j++) {AIFace face = mesh.mFaces().get(j);indices.put(face.mIndices());}indices.flip();// 生成并绑定 VBOgl.glGenBuffers(3, vboId, 0);// 顶点坐标 VBOgl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vboId[0]);gl.glBufferData(GL2.GL_ARRAY_BUFFER, vertices, GL2.GL_STATIC_DRAW);gl.glVertexAttribPointer(0, 3, GL2.GL_FLOAT, false, 0, 0);// 顶点法向量 VBOgl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vboId[1]);gl.glBufferData(GL2.GL_ARRAY_BUFFER, normals, GL2.GL_STATIC_DRAW);gl.glVertexAttribPointer(1, 3, GL2.GL_FLOAT, false, 0, 0);// 面索引 VBO(Element Buffer Object)gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, vboId[2]);gl.glBufferData(GL2.GL_ELEMENT_ARRAY_BUFFER, indices, GL2.GL_STATIC_DRAW);// 解绑 VBO 和 VAOgl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);gl.glBindVertexArray(0);// 释放内存memFree(vertices);memFree(normals);memFree(indices);}// 设置一些渲染状态gl.glEnable(GL2.GL_DEPTH_TEST);gl.glEnable(GL2.GL_CULL_FACE);}@Overridepublic void display(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);gl.glBindVertexArray(vaoId[0]);gl.glEnableVertexAttribArray(0);gl.glEnableVertexAttribArray(1);// 绘制模型的每个网格for (int i = 0; i < scene.mNumMeshes(); i++) {AIMesh mesh = AIMesh.create(scene.mMeshes().get(i));gl.glDrawElements(GL2.GL_TRIANGLES, mesh.mNumFaces() * 3, GL2.GL_UNSIGNED_INT, 0);}gl.glDisableVertexAttribArray(0);gl.glDisableVertexAttribArray(1);gl.glBindVertexArray(0);}@Overridepublic void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {GL2 gl = drawable.getGL().getGL2();gl.glViewport(0, 0, width, height);gl.glMatrixMode(GL2.GL_PROJECTION);gl.glLoadIdentity();glu.gluPerspective(45.0f, (float) width / height, 0.1f, 100.0f);gl.glMatrixMode(GL2.GL_MODELVIEW);}@Overridepublic void dispose(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();gl.glDeleteVertexArrays(1, vaoId, 0);gl.glDeleteBuffers(3, vboId, 0);// 释放 Assimp 场景资源Assimp.aiReleaseImport(scene);}public static void main(String[] args) {GLCapabilities capabilities = new GLCapabilities(GLProfile.get(GLProfile.GL2));GLCanvas canvas = new GLCanvas(capabilities);ModelLoadingExample example = new ModelLoadingExample();canvas.addGLEventListener(example);JFrame frame = new JFrame("3D Model Loading in JOGL");frame.getContentPane().add(canvas);frame.setSize(800, 600);frame.setVisible(true);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}
}

在上述代码中,首先使用 Assimp 库加载 3D 模型文件(这里以 cube.obj 为例),在 init 方法中,对模型的每个网格进行处理,将顶点坐标、法向量和面索引数据分别存储到顶点缓冲对象(VBO)中,并创建顶点数组对象(VAO)来管理这些 VBO。在 display 方法中,通过绑定 VAO 和相应的 VBO,启用顶点属性数组,使用 glDrawElements 方法绘制模型的三角形面,从而将 3D 模型渲染到屏幕上。同时,在 reshape 方法中处理窗口大小改变事件,调整投影矩阵,在 dispose 方法中释放 JOGL 和 Assimp 相关的资源。

(二)创建复杂 3D 场景

要创建复杂的 3D 场景,可以结合多个模型、光照、纹理以及几何变换等技术。例如,构建一个包含多个 3D 物体(如建筑物、树木、人物等)的室外场景:

public class ComplexSceneExample implements GLEventListener {// 存储不同模型的加载和渲染实例private ModelLoadingExample buildingModel;private ModelLoadingExample treeModel;private ModelLoadingExample characterModel;public ComplexSceneExample() {buildingModel = new ModelLoadingExample("building.obj");treeModel = new ModelLoadingExample("tree.obj");characterModel = new ModelLoadingExample("character.obj");}@Overridepublic void init(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();// 初始化每个模型的 OpenGL 资源buildingModel.init(drawable);treeModel.init(drawable);characterModel.init(drawable);// 设置场景的光照效果(与之前的光照示例类似)gl.glEnable(GL2.GL_LIGHTING);gl.glEnable(GL2.GL_LIGHT0);// 设置环境光、漫反射光和光源位置等参数//...// 设置其他场景相关的 OpenGL 状态,如深度测试、面剔除等gl.glEnable(GL2.GL_DEPTH_TEST);gl.glEnable(GL2.GL_CULL_FACE);}@Overridepublic void display(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);// 绘制建筑物,并应用适当的几何变换(如平移、旋转、缩放)gl.glPushMatrix();gl.glTranslatef(-2.0f, 0.0f, -5.0f);gl.glScalef(2.0f, 2.0f, 2.0f);buildingModel.display(drawable);gl.glPopMatrix();// 绘制树木,并分布在场景中的不同位置for (int i = 0; i < 5; i++) {gl.glPushMatrix();gl.glTranslatef((float) Math.random() * 4 - 2, 0.0f, (float) Math.random() * 4 - 2);gl.glScalef(0.5f, 0.5f, 0.5f);treeModel.display(drawable);gl.glPopMatrix();}// 绘制人物,并设置其动画效果(如果模型支持动画)gl.glPushMatrix();gl.glTranslatef(2.0f, 0.0f, -3.0f);characterModel.display(drawable);gl.glPopMatrix();}// 实现 reshape 和 dispose 方法,与之前的示例类似,处理窗口大小改变和资源释放@Overridepublic void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {GL2 gl = drawable.getGL().getGL2();gl.glViewport(0, 0, width, height);gl.glMatrixMode(GL2.GL_PROJECTION);gl.glLoadIdentity();glu.gluPerspective(45.0f, (float) width / height, 0.1f, 100.0f);gl.glMatrixMode(GL2.GL_MODELVIEW);}@Overridepublic void dispose(GLAutoDrawable drawable) {GL2 gl = drawable.getGL().getGL2();// 释放每个模型的 OpenGL 资源buildingModel.dispose(drawable);treeModel.dispose(drawable);characterModel.dispose(drawable);}public static void main(String[] args) {GLCapabilities capabilities = new GLCapabilities(GLProfile.get(GLProfile.GL2));GLCanvas canvas = new GLCanvas(capabilities);ComplexSceneExample example = new ComplexSceneExample();canvas.addGLEventListener(example);JFrame frame = new JFrame("Complex 3D Scene in JOGL");frame.getContentPane().add(canvas);frame.setSize(800, 600);frame.setVisible(true);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}
}

在这个示例中,创建了一个 ComplexSceneExample 类,在其构造函数中初始化了不同的 3D 模型加载实例(假设已经有相应的模型文件)。在 init 方法中,对每个模型进行初始化,并设置场景的光照和其他 OpenGL 状态。在 display 方法中,通过多次调用每个模型的 display 方法,并结合不同的几何变换,将建筑物、树木和人物等模型放置在场景中的合适位置,从而构建出一个复杂的室外 3D 场景。同时,在 reshape 和 dispose 方法中处理窗口大小改变和资源释放的操作,确保程序的正确性和性能。

(三)优化与性能调优

随着 3D 场景的复杂性增加,性能优化变得至关重要。以下是一些 JOGL 应用程序的性能优化技巧:

  • 顶点缓冲对象(VBO)和顶点数组对象(VAO)优化
    • 合理组织顶点数据,尽量减少数据的冗余存储。例如,对于多个共享相同顶点坐标和法向量的三角形面,可以使用索引缓冲对象(Element Buffer Object,EBO)来避免重复存储顶点数据,提高内存使用效率和渲染性能。
    • 在创建和更新 VBO 和 VAO 时,遵循最佳实践。例如,尽量减少不必要的 glBindBuffer 和 glVertexAttribPointer 调用,将相关的操作集中在一起,以减少 OpenGL 状态的切换开销。
  • 纹理优化
    • 对于纹理图像,选择合适的图像格式和压缩方式。例如,使用 DXT 压缩格式(在支持的情况下)可以减少纹理数据的内存占用,同时提高纹理加载和渲染速度。
    • 合理设置纹理过滤参数(如 GL_TEXTURE_MIN_FILTER 和 GL_TEXTURE_MAG_FILTER),根据场景的需求选择合适的过滤模式,如线性过滤(GL_LINEAR)或最近邻过滤(GL_NEAREST),以平衡图像质量和性能。
  • 渲染管线优化
    • 减少不必要的渲染操作,例如通过视锥体裁剪(Frustum Culling)技术,只渲染在摄像机视锥体范围内的物体,避免对不可见物体的渲染计算,从而提高渲染效率。
    • 合理使用深度测试(GL_DEPTH_TEST)和面剔除(GL_CULL_FACE)等技术,减少不必要的片元处理,提高渲染性能。同时,注意面剔除的设置,确保正确剔除不可见的面(如背面剔除)。
  • 模型简化与细节层次(LOD)
    • 对于复杂的 3D 模型,在不影响视觉效果的前提下,可以进行简化处理,减少模型的顶点和面的数量,降低渲染开销。例如,使用一些模型简化算法,如基于边收缩的方法,生成不同细节层次的模型版本。
    • 实现细节层次(LOD)技术,根据物体与摄像机的距离,动态切换使用不同细节层次的模型,近距离使用高细节模型,远距离使用低细节模型,从而在保证视觉效果的同时,提高整体场景的渲染性能。

通过综合运用这些优化技巧,可以显著提高 JOGL 应用程序的性能,使其能够流畅地渲染复杂的 3D 场景,提供更好的用户体验。同时,性能优化是一个持续的过程,需要根据具体的应用场景和硬件条件进行不断的测试和调整,以找到最佳的性能平衡点。

七、JOGL 在不同领域的应用案例

(一)游戏开发

在 2D 和 3D 游戏开发中,JOGL 可以用于创建精美的游戏场景、角色模型和特效。例如,一款 3D 冒险游戏可以利用 JOGL 的强大渲染能力来构建逼真的游戏世界,包括地形、建筑、怪物等元素。通过加载不同的 3D 模型和应用纹理、光照效果,使游戏画面更加生动。同时,利用 JOGL 的几何变换和动画技术,实现角色的移动、攻击动作以及场景的动态变化,为玩家提供沉浸式的游戏体验。

(二)科学可视化

在科学研究领域,JOGL 可以将复杂的科学数据可视化,帮助研究人员更好地理解和分析数据。例如,在气象学中,可以使用 JOGL 绘制 3D 天气模型,展示大气环流、云层分布等信息;在生物学中,通过 JOGL 渲染分子结构模型,直观呈现蛋白质、DNA 等生物大分子的三维形态,辅助科学家进行结构分析和功能研究;在物理学中,模拟粒子系统、磁场分布等物理现象,并以 3D 图形的形式展示出来。

(三)虚拟现实(VR)和增强现实(AR)应用

随着 VR 和 AR 技术的兴起,JOGL 也能在这些领域发挥重要作用。在 VR 应用中,它可以用于创建高度沉浸式的虚拟环境,用户通过头戴式显示设备能够身临其境地感受虚拟世界中的场景和物体交互。例如,构建一个虚拟的培训场景,如飞行模拟训练,通过精确的 3D 模型渲染和实时的图形更新,模拟出真实的飞行仪表、驾驶舱环境以及外部的天空、地形等景象,配合上追踪设备获取用户的头部和手部动作,实现逼真的操控体验。

对于 AR 应用,JOGL 可以将虚拟的 3D 模型或信息叠加到现实世界的图像上,增强用户对现实场景的感知。比如在一款 AR 家居装修应用中,用户使用手机摄像头拍摄房间,JOGL 可以将各种家具的 3D 模型实时渲染并准确地放置在摄像头捕捉到的画面中,用户可以从不同角度查看家具的摆放效果,还可以对家具进行缩放、旋转等操作,帮助他们在购买前更好地规划家居布局。

(四)教育领域

在教育领域,JOGL 为创建交互式的教学工具提供了可能性。例如,在数学和几何教学中,可以利用 JOGL 开发动态的几何图形演示软件,通过直观地展示 3D 几何形状的旋转、平移、变形等操作,帮助学生更好地理解空间几何概念。对于物理学科,能够创建物理实验的模拟环境,如牛顿力学中的物体运动、碰撞实验,电学中的电路连接与电流走向等,让学生通过亲手操作虚拟实验设备,观察实验现象,深入理解物理原理,并且不用担心实际实验中的设备损坏和安全问题。

(五)建筑设计与可视化

建筑师可以使用 JOGL 来创建建筑设计的 3D 模型,并进行实时的可视化和修改。在设计阶段,能够快速地将设计草图转化为详细的 3D 建筑模型,展示建筑的外观、内部结构以及周边环境。通过 JOGL 的光影效果模拟,设计师可以直观地看到不同时间段阳光照射下建筑物的阴影变化,评估采光效果;还可以在模型中模拟人员在建筑内部的行走路径,优化空间布局和交通流线。同时,与客户沟通时,利用 JOGL 生成的高质量渲染图和实时交互演示,能够让客户更清晰地理解设计方案,提出更具体的修改意见,从而提高设计效率和客户满意度。

八、常见问题与解决方案

(一)图形显示异常

  • 问题描述:模型出现破面、闪烁、纹理拉伸或错误等情况。
  • 可能原因及解决方案
    • 顶点数据错误:检查模型的顶点坐标、法向量等数据是否正确生成和传递。可能是在模型导入或数据处理过程中出现了精度丢失或数据损坏的情况。可以使用调试工具查看顶点数据的值,确保其符合预期的几何形状和拓扑结构。
    • 纹理坐标问题:纹理拉伸或错误可能是由于纹理坐标设置不正确。确认纹理坐标的范围是否在 [0, 1] 之间,并且与纹理图像的尺寸和模型的表面对应关系是否准确。如果使用了自动生成纹理坐标的算法,检查其是否适用于当前的模型几何形状。
    • 深度测试和面剔除设置不当:破面和闪烁问题可能与深度测试或面剔除的设置有关。确保深度测试(GL_DEPTH_TEST)已正确启用,并且深度缓冲区的清除和写入操作正常。对于面剔除,检查面剔除模式(如 GL_BACKGL_FRONT 或 GL_FRONT_AND_BACK)是否符合模型的要求,避免错误地剔除了应该显示的面。

(二)性能瓶颈

  • 问题描述:应用程序在渲染复杂场景时帧率过低,出现卡顿现象。
  • 可能原因及解决方案
    • 过度绘制:检查场景中是否存在大量被遮挡但仍然被绘制的物体。可以通过使用视锥体裁剪技术,只绘制在摄像机视锥体范围内且可见的物体,减少不必要的绘制操作。同时,优化场景的层次结构,将远处的物体和近处的物体分别进行管理和绘制,避免远处的小物体频繁更新和绘制对性能的影响。
    • 低效的图形算法:某些复杂的图形算法,如光照计算、阴影生成等,如果实现方式不够高效,可能会导致性能下降。对于光照计算,可以采用更简单的光照模型(如 Phong 光照模型的简化版本)或者使用光照贴图等预计算技术,减少实时计算的开销。对于阴影生成,考虑使用基于深度贴图的阴影算法,相比于传统的阴影体积算法,它通常具有更好的性能表现。
    • 内存管理不善:频繁地创建和销毁图形对象(如纹理、VBO、VAO 等)可能会导致内存碎片化和性能下降。优化内存管理策略,尽量在程序初始化阶段创建常用的图形对象,并在整个程序生命周期中重复使用,避免不必要的内存分配和释放操作。同时,检查是否存在内存泄漏问题,确保不再使用的图形资源被正确释放。

(三)兼容性问题

  • 问题描述:应用程序在某些操作系统或显卡驱动下出现崩溃、无法启动或图形显示异常等兼容性问题。
  • 可能原因及解决方案
    • 显卡驱动版本:不同版本的显卡驱动对 OpenGL 的支持程度可能有所差异,某些较新的 JOGL 功能可能在旧版本的驱动中不被支持,或者旧的 JOGL 代码在新驱动下可能出现兼容性问题。建议更新显卡驱动到最新版本,同时查看 JOGL 的官方文档和社区论坛,了解是否存在已知的与特定显卡驱动版本相关的兼容性问题,并遵循官方的建议进行解决。
    • 操作系统差异:JOGL 在不同操作系统上的表现可能略有不同,尤其是在处理窗口系统事件、内存管理和图形上下文创建等方面。对于跨平台开发,需要进行充分的测试,确保应用程序在各个目标操作系统上都能正常运行。可以使用条件编译或运行时检测机制,针对不同操作系统的特定问题进行代码调整,例如在某些操作系统上可能需要额外的库加载步骤或环境变量设置才能正确运行 JOGL 应用程序。

九、学习资源

(一)在线教程

  • JOGL 官方网站:包含了 JOGL 的各种信息,如教程、文档和示例代码等,是学习 JOGL 的重要起点1.
  • JOGL Wiki:有大量关于 JOGL 的文章和教程,涵盖了从开发环境搭建、基础 3D 图形绘制到高级特性应用等多个主题.
  • Prutor JOGL Useful Resources:整合了 JOGL 的相关资源,包括官网、论坛、邮件列表、GitHub 仓库、书籍推荐等,为学习者提供了全面的学习途径.
  • 微信公众号文章《JOGL,一个 Java OpenGL 的画笔大师!》:对 JOGL 的核心概念、基本用法、环境搭建以及实战案例进行了介绍,还提供了一些性能提升和常见问题解决的建议.

(二)书籍

  • 《Beginning JOGL: A Practical Guide to 3D Graphics Programming with Java》:作者 Jonathan Blow,全面介绍了 JOGL 的基础知识和实践应用,适合初学者快速入门.
  • 《JOGL: The Java OpenGL Library》:作者 Romain Guy,深入讲解了 JOGL 的各种特性和功能,帮助读者深入理解和掌握 JOGL 的高级应用.
  • 《OpenGL Programming Guide: The Official Guide to Learning OpenGL, Version 4.3》:作者 Dave Shreiner、Kevin K. Suffern 和 Graham Sellers,虽然不是专门针对 JOGL 的书籍,但作为 OpenGL 的权威指南,对于理解 JOGL 所基于的 OpenGL 原理和技术非常有帮助,可辅助读者更好地学习 JOGL.
  • 《The OpenGL ES 2.0 Programming Guide》:作者 Aaftab Munshi、Dan Ginsburg 和 Dave Shreiner,主要聚焦于 OpenGL ES 2.0,对于想要在移动设备或嵌入式系统中使用 JOGL 的开发者来说,是一本很有价值的参考书籍.

(三)示例代码与项目

  • JOGL Demos:官方提供的一系列示例项目,涵盖了各种 JOGL 的应用场景和技术点,如基本图形绘制、纹理映射、光照效果、动画等,通过学习这些示例代码,能够快速掌握 JOGL 的实际应用技巧.
  • GitHub 上的 JOGL 相关项目:在 GitHub 上搜索 “JOGL”,可以找到许多开源的 JOGL 项目,这些项目可以作为学习和参考的资源,帮助读者了解不同开发者如何在实际项目中运用 JOGL,学习到一些优秀的编程实践和设计模式.

(四)论坛与社区

  • JOGL Forums:这是 JOGL 的官方论坛,开发者可以在这里提问、分享经验、交流心得,还能获取其他 JOGL 用户的帮助和建议,对于解决学习和开发过程中遇到的问题非常有帮助.
  • Stack Overflow:作为一个知名的技术问答平台,也有许多关于 JOGL 的问题和解答,通过搜索相关问题,可以找到很多有用的信息和解决方案,同时也可以自己提问,获得社区的帮助.

十、结语

JOGL 作为 Java 平台上强大的图形编程库,为开发者提供了广阔的创作空间,从基础的图形绘制到复杂的 3D 场景构建、跨领域的应用开发,它都展现出了卓越的性能和丰富的功能。通过深入学习 JOGL 的各个方面,从环境搭建、基础图形绘制、渲染进阶技术,到高级应用开发以及性能优化和常见问题解决,开发者能够逐步掌握这一工具,将创意转化为精美的图形应用程序。无论是游戏开发、科学研究、教育教学还是其他众多领域,JOGL 都有潜力成为实现创新和价值的有力武器,随着技术的不断发展和社区的持续贡献,JOGL 的应用前景也将更加广阔,期待更多的开发者能够利用 JOGL 创造出令人惊叹的图形应用作品,推动 Java 图形编程领域的不断进步和发展。

相关文章:

JOGL 从入门到精通:开启 Java 3D 图形编程之旅

一、引言 Java 作为一门广泛应用的编程语言&#xff0c;在图形编程领域也有着强大的工具和库。JOGL&#xff08;Java OpenGL&#xff09;便是其中之一&#xff0c;它为 Java 开发者提供了访问 OpenGL&#xff08;Open Graphics Library&#xff09;功能的接口&#xff0c;使得…...

scrollIntoView() 滚动到元素顶部和底部

scrollIntoView() 是一个 DOM 方法&#xff0c;通常用于将某个元素滚动到视口内&#xff08;可视区域&#xff09;。这个方法能够使目标元素在页面上可见&#xff0c;尤其是在有滚动条的容器中。当你调用 scrollIntoView() 时&#xff0c;浏览器会自动滚动页面&#xff0c;直到…...

数据预处理

数据预处理科普指南 一、数据预处理&#xff1a;开启数据价值之门的钥匙 在当今数字化浪潮中&#xff0c;数据被视作蕴含无尽宝藏的富矿。然而&#xff0c;原始数据就如同刚从矿山采出的原石&#xff0c;虽内藏珍贵信息&#xff0c;却混杂着诸多瑕疵&#xff0c;如噪声、缺失…...

关于分布式数据库需要了解的相关知识!!!

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于关于分布式数据库方面的相关内容&a…...

IP代理测试要测试哪些方面?

在数据采集的征途上&#xff0c;IP代理是我们的得力助手&#xff0c;它不仅帮助我们高效地收集信息&#xff0c;还能在保护数据安全方面发挥重要作用。但面对众多的IP代理服务&#xff0c;如何知道哪一个才是最适合我们的呢&#xff1f;这就需要我们进行一系列的测试。 速度与…...

【前沿 热点 顶会】AAAI 2025中与目标检测有关的论文

CP-DETR: Concept Prompt Guide DETR Toward Stronger Universal Object Detection&#xff08;AAAI 2025&#xff09; 最近关于通用物体检测的研究旨在将语言引入最先进的闭集检测器&#xff0c;然后通过构建大规模&#xff08;文本区域&#xff09;数据集进行训练&#xff0…...

【Unity3D】Jobs、Burst并行计算裁剪Texture3D物体

版本&#xff1a;Unity2019.4.0f1 PackageManager下载Burst插件(1.2.3版本) 利用如下代码&#xff0c;生成一个Texture3D资源&#xff0c;它只能脚本生成&#xff0c;是一个32*32*32的立方体&#xff0c;导出路径记得改下&#xff0c;不然报错。 using UnityEditor; using Uni…...

HTML与数据抓取:GET与POST​请求处理流程

请求数据最重要的三个要点 1. 请求URL 请求URL是客户端向服务器发出请求的目标地址。它指定了要访问的资源位置&#xff0c;并且可以包含路径和查询参数。URL不仅决定了请求的目标&#xff0c;还可以传递必要的信息给服务器&#xff1b; 格式&#xff1a;http(s)://域名/路径…...

TCP客户端模拟链接websocket服务端发送消息(二)

兄弟们&#xff0c;我来填坑了&#xff0c;o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o&#xff0c;前几天写了个tcp模拟websocket客户端的以为完成&#xff0c;后面需要发送消息给服务端&#xff0c;以为简单不就是一个发送消息么&#xff0c;这不是一…...

使用Quick 录屏为视频生成二维码

Quick 可以将 录屏视频、截图、录音等生成二维码或链接分享给任何人&#xff0c;通过设置访问权限&#xff0c;仅对允许的访客开放。 首先下载安装Quick客户端&#xff0c;安装完成后打开界面如下 点击 选区录制 &#xff0c;按住鼠标左键拖动选区待录制区域 释放鼠标左键&…...

harmony数据保存-数据持久化

preference的介绍 preference的使用 数据库 sqlite的使用 可以写sql语句用executsql进行增删改查. 也可以使用提供的接口&#xff08;insert&#xff0c;delete&#xff0c;update&#xff0c;query&#xff09;进行增删改查。...

【Spring AI】Spring AI Alibaba的简单使用

提示&#xff1a;文章最后有详细的参考文档。 前提条件 SpringBoot版本为3.x以上JDK为17以上申请api-key&#xff0c;地址&#xff1a;百炼平台 引入依赖 说明&#xff1a;我的springboot版本为3.2.4&#xff0c;spring-ai-alibaba-starter版本为1.0.0-M2.1(对应spring-ai版本…...

redhawk如何看高toggle rate instance

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 redhaw...

C语言学习笔记(2)

在学习前&#xff0c;需要有一定的C语言基础。不必很深入&#xff0c;只需要知道函数&#xff0c;头文件&#xff0c;指针&#xff0c;数组等的概念就可以&#xff0c;但并非0基础笔记。 由于写到后面&#xff0c;不好编辑了&#xff0c;决定分成多篇写&#xff0c;请按编号学…...

macos 隐藏、加密磁盘、文件

磁盘加密 打开磁盘工具 点击添加 设置加密参数 设置密码 查看文件 不用的时候右键卸载即可使用的时候装载磁盘&#xff0c;并输入密码即可 修改密码 解密 加密&#xff0c;输入密码即可 禁止开机自动挂载此加密磁盘 如果不禁止自动挂载磁盘&#xff0c;开机后会弹出输入…...

机器学习(二)-简单线性回归

文章目录 1. 简单线性回归理论2. python通过简单线性回归预测房价2.1 预测数据2.2导入标准库2.3 导入数据2.4 划分数据集2.5 导入线性回归模块2.6 对测试集进行预测2.7 计算均方误差 J2.8 计算参数 w0、w12.9 可视化训练集拟合结果2.10 可视化测试集拟合结果2.11 保存模型2.12 …...

TP5 动态渲染多个Layui表格并批量打印所有表格

记录&#xff1a; TP5 动态渲染多个Layui表格每个表格设置有2行表头&#xff0c;并且第一行表头在页面完成后动态渲染显示内容每个表格下面显示统计信息可点击字段排序一次打印页面上的所有表格打印页面上多个table时,让每个table单独一页 后端代码示例&#xff1a; /*** Nod…...

Random模拟概率问题

问题背景&#xff1a; 使用 random.nextInt(100) < 10 这段代码来模拟 10% 的概率&#xff0c;其中&#xff1a; random.nextInt(100) 会生成一个 0 到 99 之间的整数。 如果结果小于 10&#xff0c;则表示触发事件&#xff0c;概率为 10/100 10%。 核心问题&#xff1a;…...

适用于Synology NAS的在线办公套件:ONLYOFFICE安装指南

使用 Synology NAS 上的 ONLYOFFICE 文档&#xff0c;您能在私有云中直接编辑文本文档、电子表格、演示文稿和 PDF&#xff0c;确保工作流程既安全又高效。本指南将分步介绍如何在 Synology 上安装 ONLYOFFICE 文档。 关于 Synology Synology NAS&#xff08;网络附加存储&…...

Go的defer原理

Go 语言中的 defer 语句用于延迟执行一个函数&#xff0c;直到包含该 defer 语句的函数执行完毕时才执行。defer 主要用于资源清理、文件关闭、解锁互斥锁等操作&#xff0c;以确保这些操作在函数返回前被执行&#xff0c;无论函数是正常返回还是由于错误提前返回。 以下是 de…...

达梦数据库迁移到MySQL字段注释缺失问题处理

目录 1、环境信息 2、问题详情 3、处理方案 3.1、提取备用库字段注释 3.2、生成正式库字段注释 1、环境信息 达梦数据库版本&#xff1a;v8 MySQL数据库版本&#xff1a;5.7.11 正式库&#xff1a;通过DTS迁移后的MySQL数据库 备用库&#xff1a;表结构与正式库一致&#…...

IntelliJ Idea常用快捷键详解

文章目录 IntelliJ Idea常用快捷键详解一、引言二、文本编辑与导航1、文本编辑2、代码折叠与展开 三、运行和调试四、代码编辑1、代码补全 五、重构与优化1、重构 六、使用示例代码注释示例代码补全示例 七、总结 IntelliJ Idea常用快捷键详解 一、引言 在Java开发中&#xff…...

Java操作Xml

一、数据准备 1、文件头实体类 package com.xiaobai.xmlpractice;import lombok.AllArgsConstructor; import lombok.Data;/*** Author 王天文* Date 2024/12/18 21:46* Description: xml头*/ AllArgsConstructor Data public class Head {private String desc;private Strin…...

CI/CD是什么?

CI/CD 定义 CI/CD 代表持续集成和持续部署&#xff08;或持续交付&#xff09;。它是一套实践和工具&#xff0c;旨在通过自动化构建、测试和部署来改进软件开发流程&#xff0c;使您能够更快、更可靠地交付代码更改。 持续集成 (CI)&#xff1a;在共享存储库中自动构建、测试…...

某些iphone手机录音获取流stream延迟问题 以及 录音一次第二次不录音问题

一些型号的iphone手机录音获取流stream延迟问题 以及 录音一次第二次不录音问题 延迟问题 navigator.mediaDevices.getUserMedia({ audio: true }) .then((stream) > {console.log(stream) }&#xff09;从开始到获取stream会有将近2s的延迟 导致按下按钮开始录音 会有前…...

基础优化方法

梯度下降 学习率代表每一次沿着这个方向走多远&#xff0c; batchsize的概念 梯度下降通过不断沿着反梯度方向更新参数求解 两个重要的超参数是 batchsize 和 学习率...

linux系统上SQLPLUS的重“大”发现

SQL plus版本&#xff1a; [oraclepg-xc2 ~]$ sqlplus -v SQL*Plus: Release 19.0.0.0.0 - Production Version 19.3.0.0.0 操作系统&#xff1a;CentOS Linux 7 (Core) 数据库&#xff1a;Oracle 19c Version 19.3.0.0.0 同样的SQL脚本在windos CMD sqlplus 执行没问题。…...

【0x001F】HCI_Read_Clock_Offset命令详解

目录 一、命令概述 二、命令格式及参数说明 2.1. HCI_Read_Clock_Offset 命令格式 2.2. Connection_Handle 三、生成事件 3.1. HCI_Command_Status 事件 3.2. HCI_Read_Clock_Offset_Complete 事件 四、命令执行流程 4.1. 命令发送阶段 4.2. 命令接收与初步反馈阶段 …...

UML(统一建模语言)及其图例使用指南

UML&#xff08;统一建模语言&#xff09;及其图例使用指南 一、UML 介绍二、UML 图表1. 用例图&#xff08;Use Case Diagram&#xff09;2. 类图&#xff08;Class Diagram&#xff09;3. 对象图&#xff08;Object Diagram&#xff09;4. 序列图&#xff08;Sequence Diagra…...

AI写标书工具:高效智能的标书撰写助手——标小兔

在现代商业竞争中&#xff0c;投标是许多企业获得项目的关键途径。而标书的质量直接影响中标的几率。传统的标书撰写过程繁琐&#xff0c;且容易出错&#xff0c;尤其是涉及到大量的数据整理、文字表达和排版工作&#xff0c;这些任务既费时又容易让人产生疲劳感。为了提升工作…...

计算机考研选西电还是成电?

谢邀~先来个总结&#xff1a;电子科技大学计算机综合实力优于西安电子科技大学&#xff0c;但是&#xff0c;二者计算机学硕考研难度没有太大差距&#xff0c;而且考试难度也同属于一个水平&#xff0c;成电性价比更高一些&#xff01;推荐同学优先报考作为985的电子科技大学&a…...

全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之循环结构(for循环语句)(一)

在C编程中&#xff0c;顺序结构和分支结构是两种基本的控制流结构&#xff0c;其中顺序结构是一种最简单、最基本的控制结构&#xff0c;即代码按照从上到下的顺序逐行执行&#xff0c;每个语句按照顺序执行&#xff0c;一条语句执行完再执行下一条语句&#xff0c;依次执行所有…...

【再谈设计模式】享元模式~对象共享的优化妙手

一、引言 在软件开发过程中&#xff0c;我们常常面临着创建大量细粒度对象的情况&#xff0c;这可能会导致内存占用过高、性能下降等问题。享元模式&#xff08;Flyweight Pattern&#xff09;就像是一位空间管理大师&#xff0c;它能够在不影响功能的前提下&#xff0c;有效地…...

NestJS中使用nestjs-plugin-module实现插件系统

1. 安装依赖 npm install brewww/nestjs-plugin-module2. 定义插件接口 首先&#xff0c;我们需要定义一个插件接口&#xff0c;这个接口定义了插件需要实现的方法。 hello/plugin.interface.ts export interface HelloServicePlugin {helloworld(): string;hello(name: st…...

jvm排查问题-实践追踪问题 与思路--堆内堆外内存泄漏排查方针

概述 排查问题的一般思路是:现象 ——> 直接原因 ——>根本原因。 从问题现象出发,可以分为 应用逻辑问题、资源使用问题、虚拟机异常: 应用逻辑可能导致报错增加、死锁、程序退出等;资源问题主要集中在CPU上升和内存上升(OOM Kill);虚拟机问题通常包括GC问题、进…...

Presence:Colyseus用于管理实时分布式数据的工具

Colyseus Presence 详细介绍 Presence 是 Colyseus 中用于管理实时分布式数据的一种工具。它主要用于在多房间、多服务器或分布式部署中实现玩家的实时在线状态、数据共享和通信。Presence 提供了一套简单的 API 来处理诸如在线玩家跟踪、分布式数据存储和发布/订阅模式等功能…...

梳理你的思路(从OOP到架构设计)_认识框架(Framework) 01

目录 1、 是框架的核心要素​编辑&i> 范例1&#xff1a; 范例2&#xff1a; 范例3&#xff1a; 1、 <E&I>是框架的核心要素 在特定领域(Domain)里&#xff0c;将EIT造形的<E&I>部份有意义地组合起来&#xff0c;就成为框架(Framework)了。基本…...

【C++11】类型分类、引用折叠、完美转发

目录 一、类型分类 二、引用折叠 三、完美转发 一、类型分类 C11以后&#xff0c;进一步对类型进行了划分&#xff0c;右值被划分纯右值(pure value&#xff0c;简称prvalue)和将亡值 (expiring value&#xff0c;简称xvalue)。 纯右值是指那些字面值常量或求值结果相当于…...

字节跳动Java开发面试题及参考答案(数据结构算法-手撕面试题)

怎么判断两个链表是否相交?怎么优化? 判断两个链表是否相交可以采用多种方法。 一种方法是使用双指针。首先分别遍历两个链表,得到两个链表的长度。然后让长链表的指针先走两个链表长度差的步数。之后,同时移动两个链表的指针,每次比较两个指针是否指向相同的节点。如果指…...

科汛网校KesionEDU CheckOrder SQL注入漏洞复现

0x01 产品简介 科汛网校KesionEDU是KESION科汛开发的在线教育建站系统,支持在线直播教学、课程点播、录播授课等多种教学方式,满足不同场景下的教学需求。提供问答互动、学习点评、在线笔记等功能,增强学员与教师之间的互动交流。拥有在线考试系统,支持单选、多选、问答等…...

【ELK】ES单节点升级为集群模式--太细了!

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言准备工作1. 查看现状【单节点】2. 原节点改集群模式3. 改es配置文件&#xff0c;增加集群相关配置项4. *改docker映射的端口* 启动新节点5. docker-compose起一…...

Spring AOP是什么

目录 谈谈自己对于 AOP 的了解 Spring AOP 和 AspectJ AOP 有什么区别? AOP 常见的通知类型有哪些? 多个切面的执行顺序如何控制? 谈谈自己对于 AOP 的了解 AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关&#xff0c;却为业务模块所共同调用的逻辑…...

Ch9 形态学图像处理

Ch9 形态学图像处理 blog点此处&#xff01;<--------- 四大算子相应性质。 腐蚀、膨胀、开闭之间的含义、关系 文章目录 Ch9 形态学图像处理预备知识(Preliminaries)膨胀和腐蚀(Dilation and Erosion)腐蚀膨胀膨胀与腐蚀的对偶关系 开闭操作(Opening and Closing)开运算闭…...

epoll学习

epoll是1个服务端监听n个客户端的多路复用技术&#xff08;通讯方式socket&#xff09; epoll&#xff1a;&#xff08;事件驱动&#xff09;服务端通过管道通讯方式将有变化事件的客户端保存到1个队列 select/poll&#xff1a;&#xff08;轮询驱动&#xff09;服务端主动遍…...

《CS2》报错dxgi.dll缺失怎么办?《CS2》游戏提示dxgi.dll缺失要怎么解决?

一、dxgi.dll缺失的根源 游戏安装问题&#xff1a;dxgi.dll文件是DirectX图形接口的一部分&#xff0c;如果游戏安装不完整或安装过程中出现问题&#xff0c;可能会导致该文件缺失。 系统更新或配置变动&#xff1a;Windows操作系统的更新或某些系统配置的变动&#xff0c;有时…...

Python基础语法之元组

主页有列表&#xff0c;集合和字典的知识点&#xff08;包含对应练习^V^&#xff09; 关注我更新更多初学实例&#xff08;下一个更新元组基础练习&#xff0c;然后集中更新一下C语言&#xff09; 欢迎纠错 元组 一.元组的应用场景 思考&#xff1a;如果想要存储多个数据&am…...

如何实现 MySQL 的读写分离?

面试题 你们有没有做 MySQL 读写分离&#xff1f;如何实现 MySQL 的读写分离&#xff1f;MySQL 主从复制原理的是啥&#xff1f;如何解决 MySQL 主从同步的延时问题&#xff1f; 面试官心理分析 高并发这个阶段&#xff0c;肯定是需要做读写分离的&#xff0c;啥意思&#x…...

Vue.js框架:在线教育系统的安全性与稳定性

2.1系统开发使用的关键技术 本系统在开发中选择B/S框架进行设计&#xff0c;语言采用Java&#xff0c;数据库采用Mysql&#xff0c;并在设计中加入VUE.js技术&#xff0c;本系统的运行环境为Idea。 2.2 VUE.js技术介绍 VUE.js是一个用来开发前台界面的JavaScript框架&#xff0…...

[python SQLAlchemy数据库操作入门]-11.面向对象方式操作股票数据

哈喽,大家好,我是木头左! 通过ORM,开发者可以使用Python类来表示数据库表,从而使得数据库操作更加直观和易于维护。本文将介绍如何使用SQLAlchemy ORM来操作股票数据。 安装 SQLAlchemy 需要安装SQLAlchemy库。可以使用pip命令进行安装: pip install sqlalchemy定义股票…...

Windows 11 中利用 WSL - Linux 虚拟环境部署 ChatTTS-Enhanced 项目教程

#工作记录 在使用 Windows 系统尝试部署一些特定项目时&#xff0c;我们可能会遇到各种各样依赖包安装的问题。比如在 Windows 系统下&#xff0c;当我们想要在 Python 3.10 虚拟环境中&#xff0c;使用命令 “pip install resemble-enhance” 以及 “pip install WeTextProces…...