1. Jogl
opengl作为图形库,比较出名的库,大多是采用C++去调用相关的API接口,最近想是否可以在JVM中调用相应的OpenGL相应的Api库,在网上找了一圈,提供有相应的API库为jopl,但相应的的示例代码比较少。因此本文将实现简单的Demo,配合Java Swing使用。(ps:考虑到kotlin的语法简单些,本来采用kotlin来实现相应的调用)
2. 使用
2.1 引入相应库
在gradle中添加库文件
implementation 'org.jogamp.jogl:jogl-all:2.3.2'
implementation 'org.jogamp.gluegen:gluegen-rt-main:2.3.2'
由于jvm采用JNI的形式调用OpenGL的库文件,因此在其官网上面下载库文件 windows-amd64 ,并且 放入到项目本层目录下的native包中去。至此opengl的引入到jvm中完成。
2.2 结合swing使用
在Java 是使用opengl 仅需实现GLEventListener接口既是
package com.jwds.pointcloud.opengl
import com.jogamp.opengl.GLAutoDrawable
import com.jogamp.opengl.GLCapabilities
import com.jogamp.opengl.GLEventListener
import com.jogamp.opengl.awt.GLJPanel
class DemoPanel(private val glCapabilities: GLCapabilities): GLJPanel(glCapabilities), GLEventListener {
override fun init(drawable: GLAutoDrawable) {
TODO("Not yet implemented")
}
override fun dispose(drawable: GLAutoDrawable) {
TODO("Not yet implemented")
}
override fun display(drawable: GLAutoDrawable) {
TODO("Not yet implemented")
}
override fun reshape(drawable: GLAutoDrawable, x: Int, y: Int, width: Int, height: Int) {
TODO("Not yet implemented")
}
}
并且在主函数中调用此panel即可:
fun main(args: Array<String>) {
val profile = GLProfile.get(GLProfile.GL2)
val capabilities = GLCapabilities(profile)
val b = DemoPanel(capabilities)
//creating frame
val frame = JFrame(" Basic Frame")
frame.add(b)
frame.size = Dimension(600,400)
frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
frame.setLocationRelativeTo(null)
frame.isVisible = true
}
2.3 实现绘画三角形
根据C++的示例,在DemoPanel实现相应的三角形绘画:
class DemoPanel(private val glCapabilities: GLCapabilities): GLJPanel(glCapabilities), GLEventListener {
private val program = ShaderProgram()
private var vbo = IntBuffer.allocate(1)
private var vao = IntBuffer.allocate(1)
init {
addGLEventListener(this)
}
override fun init(drawable: GLAutoDrawable) {
val gl = drawable.gl.gL2
val code1 = """
#version 330 core
layout (location = 0) in vec3 position;
void main()
{
gl_Position = vec4(position, 1.0);
}
""".trimIndent()
val code2 = """
#version 330 core
out vec4 color;
void main()
{
color = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
""".trimIndent()
program.add(gl, ShaderCode(GL2ES2.GL_VERTEX_SHADER, 1, arrayOf(arrayOf(code1))), System.out)
program.add(gl, ShaderCode(GL2ES2.GL_FRAGMENT_SHADER, 1, arrayOf(arrayOf(code2))), System.out)
program.link(gl, System.out)
val arr = arrayOf(arrayOf(0.5f,0.5f,0.0f), arrayOf(0.5f,0f,0f), arrayOf(0f,0.5f,0f))
val buff = FloatBuffer.allocate(arr.size * 3)
arr.forEach { item -> buff.put(item.toFloatArray()) }
gl.glGenVertexArrays(1,vao)
gl.glGenBuffers(1,vbo)
gl.glBindVertexArray(vao[0])
buff.rewind()
gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER,vbo[0])
gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, (buff.capacity() * 4).toLong(),buff, GL2ES2.GL_STATIC_DRAW)
gl.glVertexAttribPointer(0, 3, GL2ES2.GL_FLOAT, false, 12, 0)
gl.glEnableVertexAttribArray(0)
gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER,0)
gl.glBindVertexArray(0)
}
override fun dispose(drawable: GLAutoDrawable) {
}
override fun display(drawable: GLAutoDrawable) {
val gl = drawable.gl.gL2
gl.glClearColor(0.2f, 0.3f, 0.3f, 1.0f)
gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT)
gl.glEnableClientState(GLES2.GL_VERTEX_ARRAY)
program.useProgram(gl, true)
gl.glBindVertexArray(vao[0])
gl.glDrawArrays(GL2ES2.GL_TRIANGLES,0,3)
gl.glBindVertexArray(0)
gl.glDisableClientState(GLES2.GL_VERTEX_ARRAY)
}
override fun reshape(drawable: GLAutoDrawable, x: Int, y: Int, width: Int, height: Int) {
}
}
如上代码,其代码思路参考C++代码参考,在opengl初始化的时候初始化相应的VAO,VBO,以及着色器代码,并且按照相应的思路将数据填充到VBO中去,并且配置VAO的解析规则。值得注意的是在Java中必须调用buff.rewind()函数,使其数据缓存重置为原点处。否则后续填充数据没有效果。
后续在display函数中进行相关的展示数据,调用gl.glDrawArrays函数将VBO中的数据进行绘画到opengl中去,自此jvm中调用相关opengl API简单的Demo已经实现,其结果如下:
3. 结语
在java中调用与C++调用的主要思路都是一样的,主要要注意的是两种语言对内存的分配问题,以及相应指针引用的问题,在java中使用Buffers对象,必须让内存归于0才方便后续的处理,而C++一般来说就没有这样的问题,因为其指针指向的就是其引用的内存的首地址