OpenGL ES -> SurfaceView + EGL实现立方体纹理贴图+透视效果

XML文件

<?xml version="1.0" encoding="utf-8"?>
<com.example.myapplication.MySurfaceView xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent" />

Activity代码

class MainActivity7 : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main7)}
}// 立方体信息数据类
data class CubeInfo(var x: Float,       // x位置var y: Float,       // y位置var angle: Float,   // 当前旋转角度var rotationSpeed: Float,  // 旋转速度var scale : Float   // 缩放
)// 添加五个立方体的数组
private val cubes = arrayOf(CubeInfo(x = -1.0f, y = 1.0f, angle = 0f, rotationSpeed = 0.3f, scale = 0.3f),CubeInfo(x = 1.0f, y = 1.0f, angle = 45f, rotationSpeed = 0.5f, scale = 0.4f),CubeInfo(x = 0f, y = 0f, angle = 90f, rotationSpeed = 0.7f, scale = 0.2f),CubeInfo(x = -1.0f, y = -1.0f, angle = 135f, rotationSpeed = 0.4f, scale = 0.5f),CubeInfo(x = 1.0f, y = -1.0f, angle = 180f, rotationSpeed = 0.2f, scale = 0.7f)
)

SurfaceView代码+渲染线程代码

class MySurfaceView(context: Context, attrs: AttributeSet) : SurfaceView(context, attrs),SurfaceHolder.Callback {init {holder.addCallback(this)}private var mEGLThread: MyEGLThread? = nullprivate var mEGLHelper = MyEGLHelper()private var mEGLRender = MyEGLRender(context)override fun surfaceCreated(holder: SurfaceHolder) {// 创建并启动渲染线程mEGLThread = MyEGLThread(holder.surface).apply {start()}}override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {mEGLThread?.changeSize(width, height)}override fun surfaceDestroyed(holder: SurfaceHolder) {mEGLThread?.release()}inner class MyEGLThread(private val mSurface: Surface) : Thread() {private var mWidth = 0private var mHeight = 0@Volatileprivate var mRunning = true@Volatileprivate var mSizeChanged = falseoverride fun run() {super.run()try {mEGLHelper.initEGL(mSurface)mEGLRender.onSurfaceCreated()while (mRunning) {// 宽高变化,回调渲染器的onSurfaceChanged方法if (mSizeChanged) {mEGLRender.onSurfaceChanged(mWidth, mHeight)mSizeChanged = false}// 渲染一帧, 回调渲染器的onDrawFrame方法mEGLRender.onDrawFrame()mEGLHelper.swapBuffer()}} catch (e: Exception) {Log.e("yang", "EGL thread error ${e.message}")}}fun changeSize(width: Int, height: Int) {mWidth = widthmHeight = heightmSizeChanged = true}fun release() {mRunning = falsemEGLRender.onSurfaceDestroyed()mEGLHelper.releaseEGL()interrupt()}}
}

EGL工具类代码

class MyEGLHelper {private lateinit var mEGL: EGL10private lateinit var mEGLDisplay: EGLDisplayprivate lateinit var mEGLContext: EGLContextprivate lateinit var mEGLSurface: EGLSurface// 初始化EGLfun initEGL(surface: Surface) {if (::mEGL.isInitialized &&::mEGLDisplay.isInitialized &&::mEGLContext.isInitialized &&::mEGLSurface.isInitialized) {Log.e("yang", "EGL already initialized")return}// 1. 获取EGL实例mEGL = EGLContext.getEGL() as EGL10// 2. 获取默认的显示设备(就是窗口)mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)takeIf { mEGLDisplay == EGL10.EGL_NO_DISPLAY }?.apply {throw RuntimeException("eglGetDisplay failed")}// 3. 初始化默认显示设备val version = IntArray(2)takeIf { !mEGL.eglInitialize(mEGLDisplay, version) }?.apply {throw RuntimeException("eglInitialize failed")}// 4. 设置显示设备的属性val display_attribute_list = intArrayOf(EGL_RED_SIZE, 8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE, 8,EGL_ALPHA_SIZE, 8,EGL_DEPTH_SIZE, 8,EGL_STENCIL_SIZE, 4,EGL_NONE)// 5. 查找配置并进行 attribute_list 的匹配, 匹配成功返回一个数组val num_config = IntArray(1)takeIf {!mEGL.eglChooseConfig(mEGLDisplay,display_attribute_list,null,0,num_config)}?.apply {throw RuntimeException("eglChooseConfig failed")}// 匹配是否成功takeIf { num_config[0] <= 0 }?.apply {throw RuntimeException("eglChooseConfig#1 failed")}val eglConfigs = arrayOfNulls<EGLConfig>(num_config[0])takeIf {!mEGL.eglChooseConfig(mEGLDisplay,display_attribute_list,eglConfigs,num_config[0],num_config)}?.apply {throw RuntimeException("eglChooseConfig#2 failed")}// 6. 创建EGLContextval context_display_list = intArrayOf(EGL_CONTEXT_CLIENT_VERSION, 3,EGL_NONE)takeIf { ::mEGLContext.isInitialized == false }?.apply {mEGLContext = mEGL.eglCreateContext(mEGLDisplay,eglConfigs[0],EGL10.EGL_NO_CONTEXT,context_display_list)}takeIf { mEGLContext == EGL10.EGL_NO_CONTEXT }?.apply {throw RuntimeException("eglCreateContext failed")}// 7. 创建EGLSurfacetakeIf { ::mEGLSurface.isInitialized == false }?.apply {mEGLSurface = mEGL.eglCreateWindowSurface(mEGLDisplay, eglConfigs[0], surface, null)}takeIf { mEGLSurface == EGL10.EGL_NO_SURFACE }?.apply {throw RuntimeException("eglCreateWindowSurface failed")}// 8. 绑定EGLContext和EGLSurfacetakeIf { !mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext) }?.apply {throw RuntimeException("eglMakeCurrent failed")}}// 释放EGLfun releaseEGL() {takeIf { ::mEGL.isInitialized }?.apply {// 解绑EGLContext和EGLSurfacemEGL.eglMakeCurrent(mEGLDisplay,EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_CONTEXT)// 释放EGLSurfacemEGL.eglDestroySurface(mEGLDisplay, mEGLSurface)// 释放EGLContextmEGL.eglDestroyContext(mEGLDisplay, mEGLContext)// 释放EGLDisplaymEGL.eglTerminate(mEGLDisplay)}}// 交换渲染数据fun swapBuffer() {takeIf { ::mEGL.isInitialized && ::mEGLDisplay.isInitialized }?.apply {takeIf { !mEGL.eglSwapBuffers(mEGLDisplay, mEGLSurface) }?.apply {throw RuntimeException("eglSwapBuffers failed")}}}
}

渲染器接口

interface EGLRender {fun onSurfaceCreated()fun onSurfaceChanged(width: Int, height: Int)fun onDrawFrame()fun onSurfaceDestroyed()
}

渲染器实现类

class MyEGLRender(private val mContext: Context) : EGLRender {var mDrawData: MyDrawData2? = nulloverride fun onSurfaceCreated() {GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)mDrawData = MyDrawData2().apply {initTexture0(mContext, R.drawable.picture)initShaderProgram()initVertexBuffer()}}override fun onSurfaceChanged(width: Int, height: Int) {GLES30.glViewport(0, 0, width, height)mDrawData?.setSurfaceSize(width, height)}override fun onDrawFrame() {GLES30.glEnable(GLES30.GL_DEPTH_TEST)GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT)mDrawData?.drawCurrentOutput()}override fun onSurfaceDestroyed() {mDrawData?.release()}
}

渲染器实现类需要的绘制数据

class MyDrawData2 {private var mProgram: Int = -1private var NO_OFFSET = 0private val VERTEX_POS_DATA_SIZE = 3private val TEXTURE_POS_DATA_SIZE = 2private val STRIDE = (VERTEX_POS_DATA_SIZE + TEXTURE_POS_DATA_SIZE) * 4 // 每个顶点的总字节数// VAO(Vertex Array Object), 顶点数组对象, 用于存储VBOprivate var mVAO = IntArray(1)// VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据private var mVBO = IntArray(1) // 只需要一个VBO// 纹理IDprivate var mTextureID = IntArray(1)// 最终变换矩阵private var mMVPMatrix = FloatArray(16)// 投影矩阵private val mProjectionMatrix = FloatArray(16)// 视图矩阵private val mViewMatrix = FloatArray(16)// 模型矩阵private val mModelMatrix = FloatArray(16)// 视口比例private var mViewPortRatio = 1f// Surface宽高private var mSurfaceWidth = 0private var mSurfaceHeight = 0// 顶点和纹理坐标合并在一个数组中// 格式:x, y, z, u, v (顶点坐标后跟纹理坐标)val vertexData = floatArrayOf(// 顶点坐标            // 纹理坐标-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,0.5f, -0.5f, -0.5f,  1.0f, 0.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,0.5f, -0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 1.0f,0.5f,  0.5f,  0.5f,  1.0f, 1.0f,-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,0.5f, -0.5f, -0.5f,  0.0f, 1.0f,0.5f, -0.5f, -0.5f,  0.0f, 1.0f,0.5f, -0.5f,  0.5f,  0.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,0.5f, -0.5f, -0.5f,  1.0f, 1.0f,0.5f, -0.5f,  0.5f,  1.0f, 0.0f,0.5f, -0.5f,  0.5f,  1.0f, 0.0f,-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,0.5f,  0.5f, -0.5f,  1.0f, 1.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f, 0.0f,-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,-0.5f,  0.5f, -0.5f,  0.0f, 1.0f)val vertexDataBuffer = ByteBuffer.allocateDirect(vertexData.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexData).position(NO_OFFSET)// 初始化着色器程序fun initShaderProgram() {val vertexShaderCode = """#version 300 esuniform mat4 uMVPMatrix; // 变换矩阵in vec4 aPosition; // 顶点坐标in vec2 aTexCoord; // 纹理坐标 out vec2 vTexCoord; void main() {// 输出顶点坐标和纹理坐标到片段着色器gl_Position = uMVPMatrix * aPosition;vTexCoord = aTexCoord;}""".trimIndent()val fragmentShaderCode = """#version 300 esprecision mediump float;uniform sampler2D uTexture_0;in vec2 vTexCoord;out vec4 fragColor;void main() {fragColor = texture(uTexture_0, vTexCoord);}""".trimIndent()// 加载顶点着色器和片段着色器, 并创建着色器程序val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)val fragmentShader = LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)mProgram = GLES30.glCreateProgram()GLES30.glAttachShader(mProgram, vertexShader)GLES30.glAttachShader(mProgram, fragmentShader)GLES30.glLinkProgram(mProgram)// 删除着色器对象GLES30.glDeleteShader(vertexShader)GLES30.glDeleteShader(fragmentShader)}// 创建VAO, VBO, IBOfun initVertexBuffer() {// 绑定VAOGLES30.glGenVertexArrays(mVAO.size, mVAO, NO_OFFSET)GLES30.glBindVertexArray(mVAO[0])// 绑定VBO - 只需要一个VBO存储所有数据GLES30.glGenBuffers(mVBO.size, mVBO, NO_OFFSET)GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[0])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertexData.size * 4,vertexDataBuffer,GLES30.GL_STATIC_DRAW)// 设置顶点属性指针 - 顶点坐标val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")GLES30.glEnableVertexAttribArray(positionHandle)GLES30.glVertexAttribPointer(positionHandle,VERTEX_POS_DATA_SIZE,GLES30.GL_FLOAT,false,STRIDE,     // 步长,每个顶点5个float (x,y,z,u,v)NO_OFFSET   // 偏移量,位置数据在前)// 设置顶点属性指针 - 纹理坐标val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")GLES30.glEnableVertexAttribArray(textureHandle)GLES30.glVertexAttribPointer(textureHandle,TEXTURE_POS_DATA_SIZE,GLES30.GL_FLOAT,false,STRIDE,                          // 步长,每个顶点5个float (x,y,z,u,v)VERTEX_POS_DATA_SIZE * 4         // 偏移量,纹理数据在位置数据之后)// 解绑VAOGLES30.glBindVertexArray(0)// 解绑VBOGLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)}// 使用着色器程序绘制图形fun drawSomething(program: Int, mvpMatrix: FloatArray) {// 解析变换矩阵val matrixHandle = GLES30.glGetUniformLocation(program, "uMVPMatrix")GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mvpMatrix, NO_OFFSET)// 绑定VAOGLES30.glBindVertexArray(mVAO[0])// 绘制图形GLES30.glDrawArrays(GLES30.GL_TRIANGLES, NO_OFFSET, vertexData.size / (VERTEX_POS_DATA_SIZE + TEXTURE_POS_DATA_SIZE))// 解绑VAOGLES30.glBindVertexArray(0)}fun setSurfaceSize(width: Int, height: Int){mSurfaceWidth = widthmSurfaceHeight = height}fun resetMatrix() {Matrix.setIdentityM(mModelMatrix, NO_OFFSET)Matrix.setIdentityM(mViewMatrix, NO_OFFSET)Matrix.setIdentityM(mProjectionMatrix, NO_OFFSET)Matrix.setIdentityM(mMVPMatrix, NO_OFFSET)}// 计算GLSurfaceView变换矩阵fun computeMVPMatrix(width: Int, height: Int, cube: CubeInfo) {mSurfaceWidth = widthmSurfaceHeight = height// 更新立方体的旋转角度cube.angle += cube.rotationSpeedcube.angle %= 360Matrix.scaleM(mModelMatrix, NO_OFFSET, cube.scale, cube.scale, cube.scale)Matrix.translateM(mModelMatrix, NO_OFFSET, cube.x, cube.y, 0f)Matrix.rotateM(mModelMatrix, NO_OFFSET, cube.angle, 0.5f, 0.5f, 0f)val isLandscape = width > heightmViewPortRatio = if (isLandscape) width.toFloat() / height else height.toFloat() / width// 计算包围图片的球半径val radius = sqrt(1f + mViewPortRatio * mViewPortRatio)val near = 0.1fval far = near + 2 * radiusval distance = near / (near + radius)// 视图矩阵View MatrixMatrix.setLookAtM(mViewMatrix, NO_OFFSET,0f, 0f, near + radius,  // 相机位置0f, 0f, 0f,             // 看向原点0f, 1f, 0f              // 上方向)// 投影矩阵Projection MatrixMatrix.frustumM(mProjectionMatrix, NO_OFFSET,if (isLandscape) (-mViewPortRatio * distance) else (-1f * distance),  // 左边界if (isLandscape) (mViewPortRatio * distance) else (1f * distance),    // 右边界if (isLandscape) (-1f * distance) else (-mViewPortRatio  * distance),  // 下边界if (isLandscape) (1f * distance) else (mViewPortRatio * distance),    // 上边界near, // 近平面far // 远平面)// 最终变换矩阵,第一次变换,模型矩阵 x 视图矩阵 = Model x View, 但是OpenGL ES矩阵乘法是右乘,所以是View x ModelMatrix.multiplyMM(mMVPMatrix,NO_OFFSET,mViewMatrix,NO_OFFSET,mModelMatrix,NO_OFFSET)// 最终变换矩阵,第二次变换,模型矩阵 x 视图矩阵 x 投影矩阵 = Model x View x Projection, 但是OpenGL ES矩阵乘法是右乘,所以是Projection x View x ModelMatrix.multiplyMM(mMVPMatrix,NO_OFFSET,mProjectionMatrix,NO_OFFSET,mMVPMatrix,NO_OFFSET)// 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系// 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变Matrix.scaleM(mMVPMatrix,NO_OFFSET,1f,-1f,1f,)}// 加载纹理fun loadTexture(context: Context, resourceId: Int): Int {val textureId = IntArray(1)// 生成纹理GLES30.glGenTextures(1, textureId, 0)// 绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId[0])// 设置纹理参数GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MIN_FILTER,GLES30.GL_LINEAR) // 纹理缩小时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MAG_FILTER,GLES30.GL_LINEAR) // 纹理放大时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充// 加载图片val options = BitmapFactory.Options().apply {inScaled = false // 不进行缩放}val bitmap = BitmapFactory.decodeResource(context.resources, resourceId, options)// 将图片数据加载到纹理中GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0)// 释放资源bitmap.recycle()// 解绑纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)Log.e("yang","loadTexture: 纹理加载成功 bitmap.width:${bitmap.width} bitmap.height:${bitmap.height}")return textureId[0]}fun enableTexture0(program: Int, id: Int) {GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, id)val textureSampleHandle = GLES30.glGetUniformLocation(program, "uTexture_0")if (textureSampleHandle != -1) {GLES30.glUniform1i(textureSampleHandle, 0)}}fun disableTexture0() {GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)}fun initTexture0(context: Context, resourceId: Int) {mTextureID[0] = loadTexture(context, resourceId)}// GLSurfaceView实时绘制fun drawCurrentOutput() {val state = saveGLState()try {GLES30.glUseProgram(mProgram)enableTexture0(mProgram, mTextureID[0])// 为每个立方体计算MVP矩阵并绘制for (cube in cubes) {resetMatrix()computeMVPMatrix(mSurfaceWidth, mSurfaceHeight, cube)drawSomething(mProgram, mMVPMatrix)}disableTexture0()} finally {restoreGLState(state)}}// 保存OpenGL状态private fun saveGLState(): GLState {val viewport = IntArray(4)val program = IntArray(1)val framebuffer = IntArray(1)GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewport, 0)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, program, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, framebuffer, 0)return GLState(viewport, program[0], framebuffer[0])}// 恢复OpenGL状态private fun restoreGLState(state: GLState) {GLES30.glViewport(state.viewport[0],state.viewport[1],state.viewport[2],state.viewport[3])GLES30.glUseProgram(state.program)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, state.framebuffer)}fun release(){// 删除VAOif (mVAO[0] != 0) {GLES30.glDeleteVertexArrays(1, mVAO, 0)mVAO[0] = 0}// 删除VBOif (mVBO[0] != 0) {GLES30.glDeleteBuffers(1, mVBO, 0)mVBO[0] = 0}// 删除纹理if (mTextureID[0] != 0) {GLES30.glDeleteTextures(1, mTextureID, 0)mTextureID[0] = 0}// 删除着色器程序if (mProgram != -1) {GLES30.glDeleteProgram(mProgram)mProgram = -1}// 清空缓冲区vertexDataBuffer.clear()}// OpenGL状态数据类data class GLState(val viewport: IntArray,val program: Int,val framebuffer: Int)object LoadShaderUtil {// 创建着色器对象fun loadShader(type: Int, source: String): Int {val shader = GLES30.glCreateShader(type)GLES30.glShaderSource(shader, source)GLES30.glCompileShader(shader)return shader}}
}

效果图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/900525.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pikachu靶场搭建教程,csfr实操

靶场安装 靶场下载地址 百度网盘下载地址和密码 百度网盘 请输入提取码 0278 github靶场下载地址 https://gitcode.com/Resource-Bundle-Collection/c7cc1 安装前提 这两个文件夹的配置文件都要进行更改修改数据库密码 D:\phpstudy_pro\WWW\pikachu\inc D:\phpstudy_pro…

浙江大学DeepSeek系列专题线上公开课第二季第四期即将上线!端云协同:让AI更懂你的小心思! - 张圣宇 研究员

今晚8点10分左右&#xff0c;端云协同&#xff1a;让AI更懂你的小心思&#xff01;浙大学者张圣宇研究员将揭秘人机交互新玩法。浙江大学DeepSeek系列专题线上公开课第二季第四期即将上线&#xff01; 讲座 主题&#xff1a; 大小模型端云协同赋能人机交互 主讲人&#xff1a…

Vue3实战三、Axios封装结合mock数据、Vite跨域及环境变量配置

目录 Axios封装、调用mock接口、Vite跨域及环境变量配置封装Axios对象调用mock接口数据第一步、安装axios&#xff0c;处理一部请求第二步、创建request.ts文件第三步、本地模拟mock数据接口第四步、测试axiosmock接口是否可以调用第五步、自行扩展 axios 返回的数据类型 axios…

Linux如何删除文件名包含无效编码字符文件

在Linux中&#xff0c;文件名包含无效编码字符或特殊不可见字符时&#xff0c;可能导致此文件无法通过常规方式选中或删除&#xff0c;可以通过下面方法处理 1、确认文件名问题 检查终端编码环境 echo $LANG # 默认应为 UTF-8&#xff08;如 en_US.UTF-8&#xff09; 查看…

Completablefuture的底层原理是什么

参考面试回答&#xff1a; 个人理解 CompletableFuture 是 Java 8 引入的一个类、它可以让我们在多线程环境中更加容易地处理异步任务。CompletableFuture 的底层原理是基于一个名为 FutureTask 的机制、结合了 监听器模式 和 等待-通知机制 来处理异步计算。 1.首先就是Com…

C/C++ 调用约定:深入理解栈与平栈

前言 在编程中&#xff0c;理解函数调用约定和栈的机制对于编写高效代码、调试程序以及进行逆向工程至关重要。本文将深入探讨 C 和 C 的调用约定&#xff0c;以及栈与平栈的相关知识。 C 调用约定 在 C 语言中&#xff0c;默认的调用约定是 cdecl。cdecl 调用约定的特点如下&…

xv6-labs-2024 lab1

lab-1 注&#xff1a;实验环境在我的汇编随手记的末尾部分有搭建教程。 0.前置 第零章 xv6为我们提供了多种系统调用&#xff0c;其中&#xff0c;exec将从某个文件里读取内存镜像(这确实是一个好的说法)&#xff0c;并且将其替换到调用它的内存空间&#xff0c;也就是这个…

属性修改器 (AttributeModifier)

主页面设置组件 import { MyButtonModifier } from ../datastore/MyButtonModifier;Entry ComponentV2 struct MainPage {// 支持用状态装饰器修饰&#xff0c;行为和普通的对象一致Local modifier: MyButtonModifier new MyButtonModifier();build() {Column() {Button(&quo…

【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 中的监控:使用 Actuator 实现健康检查

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、引子&…

类和对象(下篇)(详解)

【本节目标】 1. 再谈构造函数 2. Static成员 3. 友元 4. 内部类 5. 再次理解封装 1. 再谈构造函数 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 #include <iostream> using name…

高精度算法

高精度加法 输入两个数&#xff0c;输出他们的和&#xff08;高精度&#xff09; 输入样例 111111111111111111111111111111 222222222222222222222222222222 输出样例 333333333333333333333333333333 #include <bits/stdc.h> using namespace std;string a,b; in…

Linux开发中注意哪些操作系统安全

在 Linux 开发中&#xff0c;确保操作系统的安全至关重要。以下是一些需要注意的方面&#xff1a; 用户管理与权限控制 合理设置用户权限&#xff1a;为不同的用户和用户组分配适当的权限&#xff0c;遵循最小权限原则。避免给普通用户过多的权限&#xff0c;以免他们误操作或…

x64dbg调试python解释器

可以先写个input()这会让dbg中断在ntdll模块中&#xff0c;查看调用堆栈在系统调用结束后的打断点 然后直接断到PyObject_Vectorcall函数

✅ Ultralytics YOLO验证(Val)时自动输出COCO指标(AP):2025最新配置与代码详解 (小白友好 + B站视频)

✅ YOLO获取COCO指标(3)&#xff1a;验证(Val) 启用 COCO API 评估&#xff08;自动输出AP指标&#xff09;| 发论文必看&#xff01; | Ultralytics | 小白友好 文章目录 一、问题定位二、原理分析三、解决方案与实践案例步骤 1: 触发 COCO JSON 保存步骤 2: 确保 self.is_coc…

【嵌入式学习3】基于python的tcp客户端、服务器

目录 1、tcp客户端 2、tcp服务器 3、服务器多次连接客户端、多次接收信息 1、tcp客户端 """ tcp:客户端 1. 导入socket模块 2. 创建socket套接字 3. 建立tcp连接(和服务端建立连接) 4. 开始发送数据(到服务端) 5. 关闭套接字 """ import soc…

Linux: network: 两台直连的主机业务不通

前提环境,有一个产品的设定是两个主机之间必须是拿网线直连。但是设备管理者可能误将设置配错,不是直连。 最近遇到一个问题,说一个主机发的包,没有到对端,一开始怀疑设定的bond设备的问题,检查了bond的设置状态,发现没有问题,就感觉非常的奇怪。后来就开始怀疑两个主机…

COMSOL固体力学接触

目录 一、接触非线性及接触面积计算 一、接触非线性及接触面积计算 COMSOL接触非线性及接触面积计算_哔哩哔哩_bilibili 形成联合体&#xff0c;在定义处右键选择“建立接触对” 位移dz使用参数化扫描。 接触选择定义中设置的接触对&#xff0c;选择罚函数&#xff0c;摩擦设置…

22.OpenCV轮廓匹配原理介绍与使用

OpenCV轮廓匹配原理介绍与使用 1. 轮廓匹配的基本概念 轮廓匹配&#xff08;Contour Matching&#xff09;是计算机视觉中的一种重要方法&#xff0c;主要用于比较两个轮廓的相似性。它广泛应用于目标识别、形状分析、手势识别等领域。 在 OpenCV 中&#xff0c;轮廓匹配主要…

oracle 快速创建表结构

在 Oracle 中快速创建表结构&#xff08;仅复制表结构&#xff0c;不复制数据&#xff09;可以通过以下方法实现&#xff0c;适用于需要快速复制表定义或生成空表的场景 1. 使用 CREATE TABLE AS SELECT (CTAS) 方法 -- 复制源表的全部列和数据类型&#xff0c;但不复制数据 C…

若依原理笔记

代码生成器 源码分析 查询数据库列表 导入表结构 生成代码 修改generator.yml配置文件 代码生成器增强 Velocity模版引擎 基础语法-变量 Lombok集成 E:\javaProject\dkd-parent\dkd-generator\src\main\resources\vm\java\domain.java.vm package ${packageName}.domain;#fo…