最近公司没什么手机项目,也不知道该做些什么就研究PC上的JAVA开发,刚开始是研究OpenCV的,发现一些OpenGL的滤镜用OpenCV处理的挺慢的,就开始研究OpenGL了,网上搜了一下发现大家比较推荐用LWJGL就试着学习学习。
先说LWJGL版本,我在Github上下载的是最新的LWJGL 3.2.3
IDEA版本是2020.2
开始在IDEA上添加jar包
File->Project Structure
点Libraries再点+号选java
添加需要的jar包
我是Android开发的,所以选择了在Android上比较熟悉的OpenGLES来练手,等熟悉一些了再试试其他jar包,对IDEA不太熟悉还不会其他配置方法,先这么用着
然后我根据官方Demo里的代码试着改成我在Android上经常用的几个OpenGLES工具类
首先是EGLUtils.java
import org.lwjgl.egl.EGL;
import org.lwjgl.egl.EGLCapabilities;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.opengles.GLES;
import org.lwjgl.opengles.GLESCapabilities;
import org.lwjgl.system.MemoryStack;
import java.lang.reflect.Field;
import java.nio.IntBuffer;
import static org.lwjgl.egl.EGL10.eglGetError;
import static org.lwjgl.egl.EGL10.eglInitialize;
import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.GLFWNativeEGL.glfwGetEGLDisplay;
import static org.lwjgl.opengles.GLES20.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.NULL;
public class EGLUtils {
private long window = NULL;
public void initEGL(int width,int height,String title){
GLFWErrorCallback.createPrint().set();
if (!glfwInit()) {
throw new IllegalStateException("Unable to initialize glfw");
}
glfwDefaultWindowHints();
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
// GLFW setup for EGL & OpenGL ES
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
int WIDTH = 300;
int HEIGHT = 300;
if(width != 0 && width != WIDTH){
WIDTH = width;
}
if(height != 0 && height != HEIGHT){
HEIGHT = height;
}
String t = "";
if(title != null){
t = title;
}
window = glfwCreateWindow(WIDTH, HEIGHT, t, NULL, NULL);
if (window == NULL) {
throw new RuntimeException("Failed to create the GLFW window");
}
glfwSetKeyCallback(window, (windowHnd, key, scancode, action, mods) -> {
if (action == GLFW_RELEASE && key == GLFW_KEY_ESCAPE) {
glfwSetWindowShouldClose(windowHnd, true);
}
});
// EGL capabilities
long dpy = glfwGetEGLDisplay();
EGLCapabilities egl;
try (MemoryStack stack = stackPush()) {
IntBuffer major = stack.mallocInt(1);
IntBuffer minor = stack.mallocInt(1);
if (!eglInitialize(dpy, major, minor)) {
throw new IllegalStateException(String.format("Failed to initialize EGL [0x%X]", eglGetError()));
}
egl = EGL.createDisplayCapabilities(dpy, major.get(0), minor.get(0));
}
try {
System.out.println("EGL Capabilities:");
for (Field f : EGLCapabilities.class.getFields()) {
if (f.getType() == boolean.class) {
if (f.get(egl).equals(Boolean.TRUE)) {
System.out.println("\t" + f.getName());
}
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// OpenGL ES capabilities
glfwMakeContextCurrent(window);
GLESCapabilities gles = GLES.createCapabilities();
try {
System.out.println("OpenGL ES Capabilities:");
for (Field f : GLESCapabilities.class.getFields()) {
if (f.getType() == boolean.class) {
if (f.get(gles).equals(Boolean.TRUE)) {
System.out.println("\t" + f.getName());
}
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("GL_VENDOR: " + glGetString(GL_VENDOR));
System.out.println("GL_VERSION: " + glGetString(GL_VERSION));
System.out.println("GL_RENDERER: " + glGetString(GL_RENDERER));
// Render with OpenGL ES
glfwShowWindow(window);
}
public void setWindowSize(int width,int height){
if(window == NULL){
return;
}
glfwSetWindowSize(window,width,height);
}
public int getWindowWidth(){
if(window == NULL){
return 0;
}
int[] width = new int[1];
int[] height = new int[1];
glfwGetWindowSize(window,width,height);
return width[0];
}
public int getWindowHeight(){
if(window == NULL){
return 0;
}
int[] width = new int[1];
int[] height = new int[1];
glfwGetWindowSize(window,width,height);
return height[0];
}
public int[] getWindowSize(){
if(window == NULL){
return null;
}
int[] width = new int[1];
int[] height = new int[1];
glfwGetWindowSize(window,width,height);
return new int[]{width[0],height[0]};
}
public void setWindowTitle(String title){
if(window == NULL){
return;
}
glfwSetWindowTitle(window,title);
}
public boolean isClose(){
if(window == NULL){
return true;
}
return glfwWindowShouldClose(window);
}
public void glPollEvents(){
glfwPollEvents();
}
public void glWaitEvents(){
glfwWaitEvents();
}
public void swap() {
if(window == NULL){
return;
}
glfwSwapBuffers(window);
}
public void release(){
GLES.setCapabilities(null);
glfwFreeCallbacks(window);
glfwTerminate();
window = NULL;
}
}
然后是ShaderUtils.java
import org.lwjgl.opengles.GLES20;
public class ShaderUtils {
public static void checkGlError(String label) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
System.out.println(label + ": glError " + error);
throw new RuntimeException(label + ": glError " + error);
}
}
public static int createProgram(String vertexSource, String fragmentSource) {
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) {
return 0;
}
int program = GLES20.glCreateProgram();
if (program != 0) {
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus);
if (linkStatus[0] != GLES20.GL_TRUE) {
System.out.println( "Could not link program: ");
System.out.println(GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
public static int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled);
if (compiled[0] == 0) {
System.out.println("Could not compile shader " + shaderType + ":");
System.out.println(GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
}
最后是GLRenderer.java
import org.lwjgl.BufferUtils;
import org.lwjgl.opengles.GLES20;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
public class GLRenderer {
private int[] textures = new int[1];
private int[] bos = new int[2];
private int programId;
private int aPositionHandle;
private int aTextureCoordHandle;
private int uTextureSamplerHandle;
private int imageWidth,imageHeight;
public void initShader(BufferedImage image){
imageWidth = image.getWidth();
imageHeight = image.getHeight();
String vertexShader = "attribute vec4 aPosition;\n" +
"attribute vec2 aTexCoord;\n" +
"varying vec2 vTexCoord;\n" +
"void main() {\n" +
" vTexCoord = aTexCoord;\n" +
" gl_Position = aPosition;\n" +
"}";
String fragmentShader = "varying highp vec2 vTexCoord;\n" +
"uniform highp sampler2D uTexture;\n" +
"void main() {\n" +
" highp vec4 color = texture2D(uTexture, vec2(vTexCoord.x,1.0-vTexCoord.y));\n" +
" gl_FragColor = color;\n" +
"}";
programId = ShaderUtils.createProgram(vertexShader, fragmentShader);
aPositionHandle = GLES20.glGetAttribLocation(programId, "aPosition");
aTextureCoordHandle = GLES20.glGetAttribLocation(programId, "aTexCoord");
uTextureSamplerHandle = GLES20.glGetUniformLocation(programId, "uTexture");
final float[] vertexData = {
1f, -1f, 0f,
-1f, -1f, 0f,
1f, 1f, 0f,
-1f, 1f, 0f
};
final float[] textureVertexData = {
1f, 0f,
0f, 0f,
1f, 1f,
0f, 1f
};
FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
FloatBuffer textureVertexBuffer = ByteBuffer.allocateDirect(textureVertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(textureVertexData);
textureVertexBuffer.position(0);
GLES20.glGenBuffers(bos);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bos[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexBuffer, GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bos[1]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, textureVertexBuffer, GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glGenTextures(textures);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, imageWidth, imageHeight,0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, imageToBuffer(image));
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
private void viewportSize(int screenWidth,int screenHeight,int width,int height) {
int left, top, viewWidth, viewHeight;
float sh = screenWidth * 1.0f / screenHeight;
float vh = width * 1.0f / height;
if (sh < vh) {
left = 0;
viewWidth = screenWidth;
viewHeight = (int) (height * 1.0f / width * viewWidth);
top = (screenHeight - viewHeight) / 2;
} else {
top = 0;
viewHeight = screenHeight;
viewWidth = (int) (width * 1.0f / height * viewHeight);
left = (screenWidth - viewWidth) / 2;
}
GLES20.glViewport(left, top, viewWidth, viewHeight);
}
public void drawFrame(int screenWidth,int screenHeight){
viewportSize(screenWidth,screenHeight,imageWidth,imageHeight);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1.0f,1.0f,1.0f,1.0f);
GLES20.glUseProgram(programId);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
GLES20.glUniform1i(uTextureSamplerHandle, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bos[0]);
GLES20.glEnableVertexAttribArray(aPositionHandle);
GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false,
0, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bos[1]);
GLES20.glEnableVertexAttribArray(aTextureCoordHandle);
GLES20.glVertexAttribPointer(aTextureCoordHandle, 2, GLES20.GL_FLOAT, false, 0, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glUseProgram(0);
}
public static ByteBuffer imageToBuffer(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
int[] pixels = new int[width * height];
image.getRGB(0, 0, width, height, pixels, 0, width);
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * 4);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel = pixels[y * width + x];
buffer.put((byte) ((pixel >> 16) & 0xFF));
buffer.put((byte) ((pixel >> 8) & 0xFF));
buffer.put((byte) (pixel & 0xFF));
buffer.put((byte) ((pixel >> 24) & 0xFF));
}
}
buffer.flip();
return buffer;
}
}
代码和Android上很像,在main里运行一下
public static void main(String[] args) {
// write your code here
new Thread(){
@Override
public void run() {
super.run();
EGLUtils eglUtils = new EGLUtils();
GLRenderer renderer = new GLRenderer();
eglUtils.initEGL(500,500,"test");
BufferedImage image = null;
try {
image = ImageIO.read(new File("X:\\test\\ic_car.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
if(image != null){
renderer.initShader(image);
while (!eglUtils.isClose()){
eglUtils.glPollEvents();
int[] size = eglUtils.getWindowSize();
renderer.drawFrame(size[0],size[1]);
eglUtils.swap();
}
}
eglUtils.release();
}
}.start();
}
效果
由于还不会用IDEA上传代码到Github上就把代码都贴上了