【OpenGL ES】EGL+FBO离屏渲染
1 前言
FBO离屏渲染 中使用 GLSurfaceView 来驱动 Renderer 渲染图片,为了隐藏 GLSurfaceView,将其设置为透明的,并且宽高都设置为1。本文将使用 EGL 代替 GLSurfaceView 生成 OpenGL ES 的渲染环境,实现离屏渲染,将渲染后的图片显示在 ImageView 上。
EGL 为 OpenGL ES 提供了绘制表面(或渲染画布),是 OpenGL ES 与显示设备的桥梁,让 OpenGL ES 绘制的内容能够在呈现当前设备上。
EGL 环境创建分为以下5步:
1)创建EGLDisplay
EGLDisplay mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] versions = new int[2];
EGL14.eglInitialize(mEGLDisplay, versions,0, versions, 1);
2)创建EGLConfig
int[] mEGLConfigAttrs = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_DEPTH_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] configNum = new int[1];
EGL14.eglChooseConfig(mEGLDisplay, mEGLConfigAttrs, 0, configs, 0,1, configNum, 0);
EGLConfig mEGLConfig = configs[0];
3)创建EGLContext
int[] mEGLContextAttrs = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
EGLContext mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, mEGLContextAttrs, 0);
4)创建EGLSurface
int[] eglSurfaceAttrs = {EGL14.EGL_WIDTH, mWidth, EGL14.EGL_HEIGHT, mHeight, EGL14.EGL_NONE};
EGLSurface mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, eglSurfaceAttrs, 0);
5)绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
读者如果对 OpenGL ES 不太熟悉,请回顾以下内容:
本文完整代码资源见→EGL+FBO离屏渲染
项目目录如下:

2 案例
本案例实现了将彩色图片转换为灰色,并且使用 ImageView 显示转换后的图片。
MainActivity.java
package com.zhyan8.egl.activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import com.zhyan8.egl.R;
import com.zhyan8.egl.model.Model;
import com.zhyan8.egl.opengl.MyEGLSurface;
import com.zhyan8.egl.opengl.MyRender;
public class MainActivity extends AppCompatActivity implements Model.Callback {
private ImageView mImageView;
private MyEGLSurface mEGlSurface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = findViewById(R.id.imageView);
initEGLSurface();
mEGlSurface.requestRender();
}
private void initEGLSurface() {
mEGlSurface = new MyEGLSurface(this);
MyRender render = new MyRender(getResources());
render.setCallback(this);
mEGlSurface.init(render);
}
@Override
public void onCall(final Bitmap bitmap) {
runOnUiThread(() -> {
mImageView.setImageBitmap(bitmap);
});
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#556688">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"/>
</FrameLayout>
BaseEGLSurface.java
package com.zhyan8.egl.opengl;
import android.content.Context;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.util.DisplayMetrics;
import android.view.WindowManager;
public class BaseEGLSurface {
protected EGLDisplay mEGLDisplay;
protected EGLConfig mEGLConfig;
protected EGLContext mEGLContext;
protected EGLSurface mEGLSurface;
protected Context mContext;
protected Renderer mRenderer;
protected EglStatus mEglStatus = EglStatus.INVALID;
protected int mWidth;
protected int mHeight;
public BaseEGLSurface(Context context) {
mContext = context;
WindowManager mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
mWidth = displayMetrics.widthPixels;
mHeight = displayMetrics.heightPixels;
}
public BaseEGLSurface(Context context, int width, int height) {
mContext = context;
mWidth = width;
mHeight = height;
}
// 设置渲染器
public void setRenderer(Renderer renderer) {
mRenderer = renderer;
}
// EGLDisplay宽高发生变化
public void onSurfaceChanged(int width, int height) {
mWidth = width;
mHeight = height;
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
createSurface();
mEglStatus = EglStatus.CREATED;
}
// 请求渲染
public void requestRender() {
if (mEglStatus == mEglStatus.INVALID) {
return;
}
if (mEglStatus == EglStatus.INITIALIZED) {
mRenderer.onSurfaceCreated();
mRenderer.onSurfaceChanged(mWidth, mHeight);
mEglStatus = EglStatus.CREATED;
}
if (mEglStatus == EglStatus.CREATED || mEglStatus == EglStatus.DRAW) {
mRenderer.onDrawFrame();
mEglStatus = EglStatus.DRAW;
}
}
// 创建EGL环境
public void createEGLEnv() {
createDisplay();
createConfig();
createContext();
createSurface();
makeCurrent();
}
// 销毁EGL环境
public void destroyEGLEnv() {
// 与显示设备解绑
EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
// 销毁 EGLSurface
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
// 销毁EGLContext
EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
// 销毁EGLDisplay(显示设备)
EGL14.eglTerminate(mEGLDisplay);
mEGLContext = null;
mEGLSurface = null;
mEGLDisplay = null;
}
// 1.创建EGLDisplay
private void createDisplay() {
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] versions = new int[2];
EGL14.eglInitialize(mEGLDisplay, versions,0, versions, 1);
}
// 2.创建EGLConfig
private void createConfig() {
EGLConfig[] configs = new EGLConfig[1];
int[] configNum = new int[1];
EGL14.eglChooseConfig(mEGLDisplay, mEGLConfigAttrs, 0, configs, 0,1, configNum, 0);
if (configNum[0] > 0) {
mEGLConfig = configs[0];
}
}
// 3.创建EGLContext
private void createContext() {
if (mEGLConfig != null) {
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, mEGLContextAttrs, 0);
}
}
// 4.创建EGLSurface
private void createSurface() {
if (mEGLContext != null && mEGLContext != EGL14.EGL_NO_CONTEXT) {
int[] eglSurfaceAttrs = {EGL14.EGL_WIDTH, mWidth, EGL14.EGL_HEIGHT, mHeight, EGL14.EGL_NONE};
mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, eglSurfaceAttrs, 0);
}
}
// 5.绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
private void makeCurrent() {
if (mEGLSurface != null && mEGLSurface != EGL14.EGL_NO_SURFACE) {
EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
mEglStatus = EglStatus.INITIALIZED;
}
}
// EGLConfig参数
private int[] mEGLConfigAttrs = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_DEPTH_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_NONE
};
// EGLContext参数
private int[] mEGLContextAttrs = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
// EGL状态
enum EglStatus {
INVALID, INITIALIZED, CREATED, DRAW
}
// 渲染器接口
interface Renderer {
void onSurfaceCreated();
void onSurfaceChanged(int width, int height);
void onDrawFrame();
}
}
MyEGLSurface.java
package com.zhyan8.egl.opengl;
import android.content.Context;
public class MyEGLSurface extends BaseEGLSurface {
public MyEGLSurface(Context context) {
super(context);
}
public MyEGLSurface(Context context, int width, int height) {
super(context, width, height);
}
public void init(Renderer renderer) {
setRenderer(renderer);
createEGLEnv();
}
}
MyRender.java
package com.zhyan8.egl.opengl;
import android.content.res.Resources;
import android.opengl.GLES30;
import com.zhyan8.egl.model.Model;
public class MyRender implements BaseEGLSurface.Renderer {
private Model mModel;
public MyRender(Resources resources) {
mModel = new Model(resources);
}
@Override
public void onSurfaceCreated() {
//设置背景颜色
GLES30.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
//启动深度测试
GLES30.glEnable(GLES30.GL_DEPTH_TEST);
//创建程序id
mModel.onModelCreate();
}
@Override
public void onSurfaceChanged(int width, int height) {
mModel.onModelChange(width, height);
}
@Override
public void onDrawFrame() {
GLES30.glClearColor(0.5f, 0.7f, 0.3f, 1.0f);
// 将颜色缓存区设置为预设的颜色
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
// 启用顶点的数组句柄
GLES30.glEnableVertexAttribArray(0);
GLES30.glEnableVertexAttribArray(1);
// 绘制模型
mModel.onModelDraw();
// 禁止顶点数组句柄
GLES30.glDisableVertexAttribArray(0);
GLES30.glDisableVertexAttribArray(1);
}
public void setCallback(Model.Callback callback) {
mModel.setCallback(callback);
}
}
Model.java
package com.zhyan8.egl.model;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.opengl.GLES30;
import com.zhyan8.egl.R;
import com.zhyan8.egl.utils.ArraysUtils;
import com.zhyan8.egl.utils.ShaderUtils;
import com.zhyan8.egl.utils.TextureUtils;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
public class Model {
private static final int TEXTURE_DIMENSION = 2; // 纹理坐标维度
private static final int VERTEX_DIMENSION = 3; // 顶点坐标维度
private Callback mCallback;
private Resources mResources;
private float mVertex[] = {-1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.0f};
private float[] mFboTexture = {0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f};
protected FloatBuffer mVertexBuffer;
protected FloatBuffer mFboTextureBuffer;
// 帧缓冲对象 - 颜色、深度、模板附着点,纹理对象可以连接到帧缓冲区对象的颜色附着点
private int[] mFrameBufferId = new int[1];
private int[] mTextureId = new int[2];
private int mProgramId;
private Point mBitmapSize = new Point();
public Model(Resources resources) {
mResources = resources;
mVertexBuffer = ArraysUtils.getFloatBuffer(mVertex);
mFboTextureBuffer = ArraysUtils.getFloatBuffer(mFboTexture);
}
// 模型创建
public void onModelCreate() {
mProgramId = ShaderUtils.createProgram(mResources, R.raw.vertex_shader, R.raw.fragment_shader);
TextureUtils.loadTexture(mResources, R.raw.xxx, mBitmapSize, mTextureId, mFrameBufferId);
}
// 模型参数变化
public void onModelChange(int width, int height) {
GLES30.glViewport(0, 0, mBitmapSize.x, mBitmapSize.y);
}
// 模型绘制
public void onModelDraw() {
GLES30.glUseProgram(mProgramId);
// 准备顶点坐标和纹理坐标
GLES30.glVertexAttribPointer(0, VERTEX_DIMENSION, GLES30.GL_FLOAT, false, 0, mVertexBuffer);
GLES30.glVertexAttribPointer(1, TEXTURE_DIMENSION, GLES30.GL_FLOAT, false, 0, mFboTextureBuffer);
// 激活纹理
GLES30.glActiveTexture(GLES30.GL_TEXTURE);
// 绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureId[0]);
// 绑定缓存
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFrameBufferId[0]);
// 绘制贴图
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
showBitmap();
}
private void showBitmap() {
// 分配字节缓区大小, 一个像素4个字节
ByteBuffer byteBuffer = ByteBuffer.allocate(mBitmapSize.x * mBitmapSize.y * Integer.BYTES);
GLES30.glReadPixels(0, 0, mBitmapSize.x, mBitmapSize.y, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, byteBuffer);
Bitmap bitmap = Bitmap.createBitmap(mBitmapSize.x, mBitmapSize.y, Bitmap.Config.ARGB_8888);
// 从缓存区读二进制缓冲数据
bitmap.copyPixelsFromBuffer(byteBuffer);
// 回调
mCallback.onCall(bitmap);
}
public void setCallback(Callback callback) {
mCallback = callback;
}
public interface Callback{
void onCall(Bitmap bitmap);
}
}
ShaderUtils.java
package com.zhyan8.egl.utils;
import android.content.res.Resources;
import android.opengl.GLES30;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ShaderUtils {
//创建程序id
public static int createProgram(Resources resources, int vertexShaderResId, int fragmentShaderResId) {
final int vertexShaderId = compileShader(resources, GLES30.GL_VERTEX_SHADER, vertexShaderResId);
final int fragmentShaderId = compileShader(resources, GLES30.GL_FRAGMENT_SHADER, fragmentShaderResId);
return linkProgram(vertexShaderId, fragmentShaderId);
}
//通过外部资源编译着色器
private static int compileShader(Resources resources, int type, int shaderId){
String shaderCode = readShaderFromResource(resources, shaderId);
return compileShader(type, shaderCode);
}
//通过代码片段编译着色器
private static int compileShader(int type, String shaderCode){
int shader = GLES30.glCreateShader(type);
GLES30.glShaderSource(shader, shaderCode);
GLES30.glCompileShader(shader);
return shader;
}
//链接到着色器
private static int linkProgram(int vertexShaderId, int fragmentShaderId) {
final int programId = GLES30.glCreateProgram();
//将顶点着色器加入到程序
GLES30.glAttachShader(programId, vertexShaderId);
//将片元着色器加入到程序
GLES30.glAttachShader(programId, fragmentShaderId);
//链接着色器程序
GLES30.glLinkProgram(programId);
return programId;
}
//从shader文件读出字符串
private static String readShaderFromResource(Resources resources, int shaderId) {
InputStream is = resources.openRawResource(shaderId);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder sb = new StringBuilder();
try {
while ((line = br.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
}
TextureUtils.java
package com.zhyan8.egl.utils;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.opengl.GLES30;
import android.opengl.GLUtils;
public class TextureUtils {
// 加载纹理贴图
public static void loadTexture(Resources resources, int resourceId, Point bitmapSize, int[] textureId, int[] frameBufferId) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap bitmap = BitmapFactory.decodeResource(resources, resourceId, options);
bitmapSize.set(bitmap.getWidth(), bitmap.getHeight());
// 生成纹理id
GLES30.glGenTextures(2, textureId, 0);
for (int i = 0; i < 2; i++) {
// 绑定纹理到OpenGL
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId[i]);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
if (i == 0) {
// 第一个纹理对象给渲染管线(加载bitmap到纹理中)
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, bitmap, 0);
} else {
// 第二个纹理对象给帧缓冲区
GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(),
0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);
}
// 取消绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE);
}
// 创建帧缓存id
GLES30.glGenFramebuffers(1, frameBufferId, 0);
// 绑定帧缓存
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBufferId[0]);
// 将第二个纹理附着在帧缓存的颜色附着点上
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, textureId[1], 0);
// 取消绑定帧缓存
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE);
}
}
ArraysUtils.java
package com.zhyan8.egl.utils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
public class ArraysUtils {
public static FloatBuffer getFloatBuffer(float[] floatArr) {
FloatBuffer fb = ByteBuffer.allocateDirect(floatArr.length * Float.BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
fb.put(floatArr);
fb.position(0);
return fb;
}
}
vertex_shader.glsl
#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
out vec2 vTexCoord;
void main() {
gl_Position = vPosition;
vTexCoord = aTextureCoord;
}
fragment_shader.glsl
#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 fragColor;
void main() {
vec4 color = texture(uTextureUnit, vTexCoord);
float rgb = color.g;
vec4 c = vec4(rgb, rgb, rgb, color.a);
fragColor = c;
}
3 运行效果
原图:

处理后:

声明:本文转自【OpenGL ES】EGL+FBO离屏渲染
【OpenGL ES】EGL+FBO离屏渲染的更多相关文章
- IOS 中openGL使用教程4(openGL ES 入门篇 | 离屏渲染)
通常情况下,我们使用openGL将渲染好的图片绘制到屏幕上,但有时候我们不想显示处理结果,这时候就需要使用离屏渲染了. 正常情况下,我们将屏幕,也就是一个CAEAGLLayer对象作为渲染目标,离屏渲 ...
- Opengl ES之FBO
FBO介绍 FBO帧缓冲对象,它的主要作用一般就是用作离屏渲染,例如做Camera相机图像采集进行后期处理时就可能会用到FBO.假如相机出图的是OES纹理,为了方便后期处理, 一般先将OES纹理通过F ...
- 一步步实现windows版ijkplayer系列文章之六——SDL2源码分析之OpenGL ES在windows上的渲染过程
一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...
- NDK OpenGLES3.0 开发(五):FBO 离屏渲染
什么是 FBOFBO(Frame Buffer Object)即帧缓冲区对象,实际上是一个可添加缓冲区的容器,可以为其添加纹理或渲染缓冲区对象(RBO). FBO 本身不能用于渲染,只有添加了纹理或者 ...
- OpenGL ES 3.0之Fragment buffer objects(FBO)详解 (转)
http://www.cnblogs.com/salam/p/4957250.html 片段操作图 这篇文章将介绍从写入帧缓冲和读取帧缓冲的方式. Buffers(缓冲) OpenGL ES支持三种缓 ...
- Opengl ES之YUV数据渲染
YUV回顾 记得在音视频基础知识介绍中,笔者专门介绍过YUV的相关知识,可以参考: <音视频基础知识-YUV图像> YUV数据量相比RGB较小,因此YUV适用于传输,但是YUV图不能直接用 ...
- OpenGL ES 3.0之Fragment buffer objects(FBO)详解(一)
片段操作图 这篇文章将介绍从写入帧缓冲和读取帧缓冲的方式. Buffers(缓冲) OpenGL ES支持三种缓冲: OpenGL ES •• Color buffer颜色缓冲 •• Depth bu ...
- 【Android应用开发】 OpenGL ES -- 透视投影 和 正交投影
博客地址 : http://blog.csdn.net/shulianghan/article/details/46680803 源代码下载 : http://download.csdn.net/de ...
- Opengl ES之踩坑记
前因 最近在尝试使用Opengl ES实现一些LUT滤镜效果,在实现这些滤镜效果的时候遇到一些兼容性的坑,踩过这些坑后我希望把这几个坑分享给读者朋友们, 希望同在学习Opengl ES的朋友们能少走弯 ...
- Opengl ES之矩阵变换(上)
前言 说到矩阵变换,我们第一时间想到的就是大学时代的线性代数这些复杂的东西,突然有了一种令人从入门到放弃的念头,不慌,作为了一个应用层的CV工程师, 在实际应用中线性代数哪些复杂的计算根本不用我们自己 ...
随机推荐
- VUE - 配置跨域
'/api': { target: 'http://localhost:8088/', //这里后台的地址模拟的;应该填写你们真实的后台接口 changOrigin: true, //允许跨域 pat ...
- java - 字节流读取文件
package stream; import java.io.*; public class InputStreamReaderString { public static void main(Str ...
- [转帖]是的你没看错,HTTP3来了
https://www.jianshu.com/p/288ce6a8ab88 简介 很多小伙伴可能还沉浸在HTTP1.1的世界无法自拔,但是时代的洪流已经带领我们来到了HTTP3的世界了.是的,你在桥 ...
- [转帖]12.24.2 DECIMAL Data Type Characteristics
https://dev.mysql.com/doc/refman/8.0/en/fixed-point-types.html This section discusses the characteri ...
- 【K哥爬虫普法】你很会写爬虫吗?10秒抢票、10秒入狱,了解一下?
我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K 哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识, ...
- [置顶] k8s,docker,微服务,监控
综合 第一篇:k8s服务A内部调用服务B的方式 第二篇:go-zero grpc 第一篇:grpc,protobuf安装 第二篇:grpc签发证书 第三篇:golang-grpc 第四篇:python ...
- C# MVC+NHibernate 分页
一.页面代码,分为三部分,一是查询条件部分,二是数据部分,二是页码条 <div id="ticketoutquery"> <table> <tr> ...
- OCR文字检测与识别系统:融合文字检测、文字识别和方向分类器的综合解决方案
1. OCR文字检测与识别系统:融合文字检测.文字识别和方向分类器的综合解决方案 前两章主要介绍了DBNet文字检测算法以及CRNN文字识别算法.然而对于我们实际场景中的一张图像,想要单独基于文字检测 ...
- 深度学习应用篇-推荐系统[11]:推荐系统的组成、场景转化指标(pv点击率,uv点击率,曝光点击率)、用户数据指标等评价指标详解
深度学习应用篇-推荐系统[11]:推荐系统的组成.场景转化指标(pv点击率,uv点击率,曝光点击率).用户数据指标等评价指标详解 1. 推荐系统介绍 在网络技术不断发展和电子商务规模不断扩大的背景下, ...
- 驱动开发:WinDBG 枚举SSDT以及SSSDT地址
在前面的博文<驱动开发:内核读取SSDT表基址>中已经教大家如何寻找SSDT表基地址了,今天给大家分享两个适用于WinDBG调试器上的脚本文件,该脚本文件可以很好的枚举出当前系统内的SSD ...