Android OpenGLES 360全景圖片渲染(球體內部)

在這里插入圖片描述

概述

????360度全景圖是一種虛擬現實技術,它通過對現實場景進行多角度拍攝后,利用計算機軟件將這些照片拼接成一個完整的全景圖像。這種技術能夠讓觀看者在虛擬環境中以交互的方式查看整個周圍環境,就好像他們真的站在那個位置一樣。在Android設備上查看360度全景圖片,可以使用一些專門的app, 不如Google相冊, Google 街景, 第三方的全景圖片查看應用。這些應用程序能夠識別并以交互方式展示360度全景圖像,讓用戶可以旋轉、縮放和平移來探索整個場景。
????360全景圖片渲染可以使用openGLES來輕松實現.

實現

在這里插入圖片描述
在這里插入圖片描述

使用OpenGL 創建一個球體, 并將圖片紋理按一定的規則貼到球體的內部. 如上圖, 可以考慮將圖片等比例鋪滿球面展開的面積即可, 方法有很多, 可以按經度, 也可以按維度, 不同順序對結果沒影響.

參考代碼:

主界面

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import com.android.apitester.SphereView;public class SphereViewer extends Activity {SphereView sphereView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Bitmap bm = BitmapFactory.decodeFile("/sdcard/sphere.jpg");sphereView = new SphereView(this);sphereView.setBitmap(bm);setContentView(sphereView);}
}

自定義GLSurfaceView

import android.content.Context;
import android.graphics.Bitmap;
import android.hardware.SensorManager;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.View;import com.ansondroider.acore.Logger;
import com.ansondroider.acore.opengl.EGLHelper;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;public class SphereView  extends GLSurfaceView implements GLSurfaceView.Renderer {final String TAG = "SphereView";private String vertextShaderSource = "attribute vec4 aPosition;" +"precision mediump float;" +"uniform mat4 uMatrix;" +"attribute vec2 aCoordinate;" +"varying vec2 vCoordinate;" +"attribute float alpha;" +"varying float inAlpha;" +"void main(){" +"	gl_Position = uMatrix*aPosition;\n" +//"	gl_Position = aPosition;" +" 	gl_PointSize = 10.0;" +"	vCoordinate = aCoordinate;" +"	inAlpha = alpha;" +"}";private String fragmentShaderSource = "#extension GL_OES_EGL_image_external : require\n" +"precision mediump float;" +"varying vec2 vCoordinate;" +"varying float inAlpha;" +//"uniform samplerExternalOES uTexture;" +"uniform sampler2D uTexture;" +"uniform bool isPoint;" +"void main() {" +"	vec4 color = texture2D(uTexture, vCoordinate);" +"	if(isPoint){" +" 	   gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);" +"	}else{" +"		gl_FragColor = vec4(color.r, color.g, color.b, inAlpha);" +"	}" +"}";//紋理IDprivate int mTextureId = -1;// 定義OpenGL 程序IDprivate int mProgram = -1;//矩陣變換接受者(shader中)private int mVertexMatrixHandler = -1;//頂點坐標接收者private int mVertexPosHandler = -1;//紋理坐標接受者private int mTexturePosHandler = -1;//紋理接受者private int mTextureHandler = -1;//半透明值接受者private int mAlphaHandler = -1;private int mPointHandler = -1;//矩陣private float[] mMatrix = new float[16];private float[] projectionMatrix = new float[16];//透明度private float mAlpha = 1f;public SphereView(Context context) {super(context);//A        #16 pc 000000000000d348  [anon:dalvik-classes2.dex extracted in memory from /data/app/~~Kkld4fhSS0RUjHOLDPgB7w==/com.ansondroider.apitester-9-2nRYL85FvddCKlwbFEFw==/base.apk!classes2.dex] (com.ansondroider.apitester.sphere.SphereView.createGLPrg+36)//A        #18 pc 000000000000d74a  [anon:dalvik-classes2.dex extracted in memory from /data/app/~~Kkld4fhSS0RUjHOLDPgB7w==/com.ansondroider.apitester-9-2nRYL85FvddCKlwbFEFw==/base.apk!classes2.dex] (com.ansondroider.apitester.sphere.SphereView.onDrawFrame+138)setEGLContextClientVersion(2);setRenderer(this);setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {updateRotate(event);return true;}});}//if touching, stop sensor rotate.boolean touching;float dx, dy;float angStartX, angStartY;float angelTouchX, angelTouchY;public void updateRotate(MotionEvent e){if(e.getAction() == MotionEvent.ACTION_DOWN){touching = true;dx = e.getX();dy = e.getY();//float[] rot = {0, 0, 0};//provider.getAngle(rot);angStartX = angelTouchY;//rot[0];angStartY = angelTouchX;//rot[1];}else if(e.getAction() == MotionEvent.ACTION_MOVE){angelTouchY = angStartX + 180f * (e.getX() - dx) / getWidth();angelTouchX = angStartY + 180f * (e.getY() - dy) / getHeight();}else{touching = false;}}Bitmap tmpBm = null;boolean rebindTexture = false;public void setBitmap(Bitmap bm){//check mTextureId is -1, save bm to tmpBm;if(bm == null || bm.isRecycled())return;else tmpBm = bm;if(mTextureId >= 0){rebindTexture = true;}}float ratio = 1;//float frustumSize 	= 2.5f;float[] NEAR_FAR 	= {1.7f,	0.5f, 	20f, 	17f};float[] EYE_Z 		= {-0.06f, 	-3f, 	3};float[] SCALE 		= {5f, 		0.5f, 	10};float[] FRUSTUM 	= {1, 		1, 		3};float[] RADIUS 		= {2.5f, 	2, 		3};/*** 初始化矩陣變換,主要是防止視頻拉伸變形*/private void initDefMatrix() {//Log.d(TAG, "initDefMatrix");//設置相機位置float[] viewMatrix = new float[16];Matrix.setLookAtM(viewMatrix, 0,0f, 0f, EYE_Z[0],0f, 0f, 0,0f, 1.0f, 0f);float[] rotSensor = new float[16];Matrix.setIdentityM(rotSensor, 0);float[] rotTouch = new float[16];Matrix.setIdentityM(rotTouch, 0);Matrix.rotateM(rotTouch, 0, -angelTouchX, 1, 0, 0);float rotY = -angelTouchY + (vtSphere != null ? vtSphere.getStartRotateY() : 0);Matrix.rotateM(rotTouch, 0, rotY, 0, 1, 0);Matrix.multiplyMM(viewMatrix, 0, rotSensor, 0, rotTouch, 0);// 參數解釋://result: 存儲乘法結果的矩陣數組。//resultOffset: 存儲結果矩陣的數組起始偏移量。//lhs: 左操作數矩陣(left-hand side)的數組。//lhsOffset: 左操作數矩陣的數組起始偏移量。//rhs: 右操作數矩陣(right-hand side)的數組。//rhsOffset: 右操作數矩陣的數組起始偏移量。//Matrix.multiplyMM(mMatrix, 0, projectionMatrix, 0, viewMatrix, 0);Matrix.multiplyMM(mMatrix, 0, projectionMatrix, 0, viewMatrix, 0);Matrix.scaleM(mMatrix, 0, SCALE[0], SCALE[0], SCALE[0]);}@Overridepublic void setAlpha(float alpha) {super.setAlpha(alpha);mAlpha = alpha;}/*** 創建并使用opengles程序*/private void createGLPrg() {//Logger.d(TAG, "createGLPrg");if (mProgram == -1) {int vertexShader = EGLHelper.compileVertexShader(vertextShaderSource);int fragmentShader = EGLHelper.compileFragmentShader(fragmentShaderSource);//創建programe陳谷mProgram = GLES20.glCreateProgram();//將頂點著色器加入程序GLES20.glAttachShader(mProgram, vertexShader);//將片元著色器加入程序GLES20.glAttachShader(mProgram, fragmentShader);GLES20.glLinkProgram(mProgram);//從程序中獲取句柄mVertexMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMatrix");mVertexPosHandler = GLES20.glGetAttribLocation(mProgram, "aPosition");mTextureHandler = GLES20.glGetUniformLocation(mProgram, "uTexture");mTexturePosHandler = GLES20.glGetAttribLocation(mProgram, "aCoordinate");mAlphaHandler = GLES20.glGetAttribLocation(mProgram, "alpha");mPointHandler = GLES20.glGetUniformLocation(mProgram, "isPoint");}//使用opengl程序if (mProgram != -1) GLES20.glUseProgram(mProgram);}@Overrideprotected void onDetachedFromWindow() {Logger.d(TAG, "onDetachedFromWindow");super.onDetachedFromWindow();GLES20.glDisableVertexAttribArray(mVertexPosHandler);GLES20.glDisableVertexAttribArray(mTexturePosHandler);GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);GLES20.glDeleteTextures(1, new int[]{mTextureId}, 0);GLES20.glDeleteProgram(mProgram);}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {Logger.d(TAG, "onSurfaceCreated");}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {Logger.d(TAG, "onSurfaceChanged");GLES20.glViewport(0, 0, width, height);ratio = (float) width / height;//m: 存儲結果矩陣的數組。//offset: 存儲結果矩陣的數組起始偏移量。//left	: 近平面左邊界的 X 坐標值。//right	: 近平面右邊界的 X 坐標值。//bottom: 近平面下邊界的 Y 坐標值。//top	: 近平面上邊界的 Y 坐標值。//near	: 近平面的 Z 坐標值(必須為正數)。//far	: 遠平面的 Z 坐標值(必須為正數)。Matrix.frustumM(projectionMatrix, 0,-ratio*FRUSTUM[0], ratio*FRUSTUM[0],-FRUSTUM[0], FRUSTUM[0],NEAR_FAR[0], NEAR_FAR[3]);}void prepareTexture(){if((mTextureId < 0 || rebindTexture) && tmpBm != null){Logger.d(TAG, "setBitmap bind bitmap to texture " + mTextureId);if(mTextureId >= 0) {//check texture is bind, unbind.GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);GLES20.glDeleteTextures(1, new int[]{mTextureId}, 0);}//create texture and bind bitmap to itint[] textures = new int[1];GLES20.glGenTextures(1, textures, 0);mTextureId = textures[0];rebindTexture = false;//mTextureId is not -1, then bind Bitmap to mTextureId// 將 Bitmap 加載到紋理GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);// 設置紋理參數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.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, tmpBm, 0);tmpBm.recycle(); // 釋放 Bitmap 資源}}@Overridepublic void onDrawFrame(GL10 gl) {//Logger.d(TAG, "onDrawFrame");GLES20.glClearColor(bgRGBA[0], bgRGBA[1], bgRGBA[2], bgRGBA[3]);long ct = SystemClock.uptimeMillis();if(ct - last_fps > 1000) {last_fps = ct;fps = 0;}fps ++;prepareTexture();//drawBitmap_initBuffer();//清除顏色緩沖和深度緩沖GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);///GLES20.glEnable(GLES20.GL_DEPTH_TEST);if (mTextureId != -1) {initDefMatrix();//2/創建、編譯、啟動opengles著色器createGLPrg();//3.激活并綁定紋理單元///activateTexture();//5.開始繪制渲染doDraw();}}float[] bgRGBA = {0, 0, 0, 1};int fps = 0;long last_fps = SystemClock.uptimeMillis();/*** 開始繪制渲染*/SphereTexture vtSphere;public void doDraw() {//Logger.d(TAG, "doDraw");if(mMatrix == null)return;if(vtSphere == null){vtSphere = new SphereTexture(RADIUS[0], 32, 32,false);}// 綁定紋理GLES20.glActiveTexture(GLES20.GL_TEXTURE0);GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);GLES20.glUniform1i(mTextureHandler, 0);GLES20.glUniformMatrix4fv(mVertexMatrixHandler, 1, false, mMatrix, 0);GLES20.glVertexAttrib1f(mAlphaHandler, mAlpha);GLES20.glUniform1i(mPointHandler, vtSphere.isPoint() ? 1 : 0);vtSphere.draw(mVertexPosHandler, mTexturePosHandler);}}//Class SphereView end

球面頂點和紋理坐標的生成與渲染

import android.graphics.RectF;
import android.opengl.GLES20;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;public class SphereTexture extends VertexTexture{private float mRadius;private int mLat, mLong;private boolean mIs180;private float[] degreeLongitude;private float[] degreeLatitude;public SphereTexture(float radius, int latitudeBands, int longitudeBands,boolean is180) {mRadius = radius;mLat = latitudeBands;mLong = longitudeBands;mIs180 = is180;init();}@Overridepublic float getStartRotateY() {return 0;//mIs180 ? 180f: -90f;}@Overrideprotected void initVertexTextureCoordinate(){if(degreeLongitude == null) {degreeLongitude = new float[]{-_PI, 	_PI, 	_PI * 2};degreeLatitude 	= new float[]{-0, 		_PI, 	_PI};}mVertexCoors = new float[(mLat + 1) * (mLong + 1) * 3];mTextureCoors = new float[(mLat + 1) * (mLong + 1) * 2];mIndices = new short[mLat * mLong * 6];int vIndex = 0;int tIndex = 0;int iIndex = 0;for (int lat = 0; lat <= mLat; lat++) {float theta = degreeLatitude[0] +  (float) lat / mLat * degreeLatitude[2];float sinTheta = (float) Math.sin(theta);float cosTheta = (float) Math.cos(theta);for (int lon = 0; lon <= mLong; lon++) {float phi = degreeLongitude[0] + (float) lon / mLong * degreeLongitude[2];//float phi =  (float) lon / longitudeBands * 2 * (MAX_DEGREE_X);float sinPhi = (float) Math.sin(phi);float cosPhi = (float) Math.cos(phi);float x = cosPhi * sinTheta;float y = cosTheta;float z = sinPhi * sinTheta;mVertexCoors[vIndex] = mRadius * x;mVertexCoors[vIndex + 1] = mRadius * y;mVertexCoors[vIndex + 2] = mRadius * z;// 生成紋理坐標float u = (float) lon / mLong;float v = (float) lat / mLat;// 生成紋理坐標/*mTextureCoors[tIndex] = u;//xmTextureCoors[tIndex + 1] = v;//y*/RectF r = getTextureArea();mTextureCoors[tIndex] = r.left + r.width() * u;//xmTextureCoors[tIndex + 1] = r.top + r.height() * v;//yif (lat < mLat && lon < mLong) {int first = lat * (mLong + 1) + lon;int second = first + mLong + 1;mIndices[iIndex++] = (short) first;mIndices[iIndex++] = (short) second;mIndices[iIndex++] = (short) (first + 1);mIndices[iIndex++] = (short) second;mIndices[iIndex++] = (short) (second + 1);mIndices[iIndex++] = (short) (first + 1);/*first		first + 1		...|----------|-------------||		   |			 ||		   |			 |second|----------|-------------||       second + 1       ||          |             ||          |             ||----------|-------------|*/}vIndex += 3;tIndex += 2;}}}@Overridepublic void updateZ(float z) {super.updateZ(z);mRadius = z;initVertexTextureCoordinate();}@Overridepublic void draw(int mVertexPosHandler, int mTexturePosHandler) {// 綁定頂點緩沖區GLES20.glEnableVertexAttribArray(mVertexPosHandler);mVertexBuffer.position(0);GLES20.glVertexAttribPointer(mVertexPosHandler, 3, GLES20.GL_FLOAT, false, 0, mVertexBuffer);// 綁定紋理坐標緩沖區GLES20.glEnableVertexAttribArray(mTexturePosHandler);mTextureBuffer.position(0);GLES20.glVertexAttribPointer(mTexturePosHandler, 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer);// 綁定紋理//if(textureId > -1)GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);// 繪制球體if(!isPoint()) {GLES20.glDrawElements(GLES20.GL_TRIANGLES, getIndexCount(),GLES20.GL_UNSIGNED_SHORT, mIndexBuffer);}elseGLES20.glDrawArrays(GLES20.GL_POINTS, 0, getVertexCount());GLES20.glDisableVertexAttribArray(mVertexPosHandler);GLES20.glDisableVertexAttribArray(mTexturePosHandler);}}

最終效果:
在這里插入圖片描述

參考

  1. Android OpenGLES3繪圖:球形視頻播放器
  2. Android 使用 OpenGL ES 繪制球面

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/74123.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/74123.shtml
英文地址,請注明出處:http://en.pswp.cn/web/74123.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

代碼隨想錄算法訓練營第三十二天 | 509.斐波那契數 70.爬樓梯 746.使用最小花費爬樓梯

509. 斐波那契數 題目鏈接&#xff1a;509. 斐波那契數 - 力扣&#xff08;LeetCode&#xff09; 文章講解&#xff1a;代碼隨想錄 視頻講解&#xff1a;手把手帶你入門動態規劃 | LeetCode&#xff1a;509.斐波那契數_嗶哩嗶哩_bilibili 思路&#xff1a;輸入&#xff1a;…

拼多多 anti-token unidbg 分析

聲明: 本文章中所有內容僅供學習交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包內容、敏感網址、數據接口等均已做脫敏處理&#xff0c;嚴禁用于商業用途和非法用途&#xff0c;否則由此產生的一切后果均與作者無關&#xff01; 逆向分析 版本7.3-7.4 都試過加密沒什…

【工具】BioPred一個用于精準醫療中生物標志物分析的 R 軟件包

介紹 R 語言包 BioPred 提供了一系列用于精準醫療中的亞組分析和生物標志物分析的工具。它借助極端梯度提升&#xff08;XGBoost&#xff09;算法&#xff0c;并結合傾向得分加權和 A 學習方法&#xff0c;幫助優化個體化治療規則&#xff0c;從而簡化亞組識別過程。BioPred 還…

橫掃SQL面試——時間序列分組與合并(會話劃分)問題

橫掃SQL面試題 &#x1f4cc; 時間序列分組與合并問題 &#x1f4da; 橫掃SQL面試——時間序列分組與合并解析 &#x1f31f; 核心問題類型 時間序列分組&#xff08;Sessionization&#xff09; 處理具有時間維度的連續數據流&#xff0c;根據特定規則&#xff08;如時間間隔…

PCB鉆孔之多邊形孔分析

問題分析 在鉆孔過程中&#xff0c;鉆頭的運動可以分為兩部分&#xff1a; 公轉&#xff1a;鉆頭的軸線繞理想軸線&#xff08;鉆孔中心線&#xff09;做圓周運動。自轉&#xff1a;鉆頭繞自身軸線做旋轉運動。 由于公轉和自轉的疊加&#xff0c;鉆尖的運動軌跡會形成復雜的…

Android源碼之App啟動

目錄 App啟動概述 App啟動過程 App啟動過程圖 源碼概述 跨進程啟動 進程內啟動 下面以應用桌面Launcher啟動App的MainActivity來舉例&#xff1a; App啟動概述 首先&#xff0c;MainActivity是由Launcher組件來啟動的&#xff0c;而Launcher又是通過Activity管理服務Act…

指紋瀏覽器技術解析:如何實現多賬號安全運營與隱私保護

瀏覽器指紋的挑戰與需求 在數字化運營場景中&#xff0c;瀏覽器指紋技術被廣泛用于追蹤用戶行為。通過采集設備硬件參數&#xff08;如屏幕分辨率、操作系統&#xff09;、軟件配置&#xff08;如字體、插件&#xff09;及網絡特征&#xff08;如IP地址、時區&#xff09;&…

生活電子常識——cmd不能使用anaconda的python環境,導致輸入python打開應用商店

前言 電腦已經安裝了anaconda,從自帶的Anaconda Prompt (Anaconda3)中是可以識別python環境的&#xff0c;然而切換到cmd時&#xff0c;突然發現cmd中無法識別anaconda的python環境&#xff0c;竟然打開了應用商店讓我安裝Python&#xff0c;這當然是不對的。 解決 這是因為…

搭建前端環境和后端環境

搭建前端環境 ①、安裝vscode&#xff0c;并安裝相應的插件工具 ②、安裝node.js&#xff0c;可以選擇當前版本&#xff0c;或者其他版本 ③、創建工作區 創建一個空文件夾&#xff0c;然后通過vscode工具打開&#xff0c;保存為后綴名為.code-workspace ④、從gitee…

Java基礎知識總結(1.8)——Java 注解(持續更新)

更新時間&#xff1a;2025-03-31 Web后端專欄&#xff1a;CSDN專欄——理論-Web后端技術博客總目錄&#xff1a;計算機技術系列博客——目錄頁 8.1 注解的概念 8.1.1 定義與作用 Java注解&#xff08;Annotation&#xff09;是Java語言自JDK1.5版本引入的核心特性&#xff0…

線程概念與控制(下)

線程概念與控制&#xff08;中&#xff09;https://blog.csdn.net/Small_entreprene/article/details/146539064?sharetypeblogdetail&sharerId146539064&sharereferPC&sharesourceSmall_entreprene&sharefrommp_from_link對于之前學習的內容&#xff0c;我們…

SQL注入之盲注技術詳解

SQL注入之盲注技術詳解 一、盲注基本概念盲注特點&#xff1a; 二、盲注主要類型1. 布爾盲注判斷依據&#xff1a; 2. 時間盲注判斷依據&#xff1a; 三、布爾盲注詳細技術1. 識別布爾盲注2. 數據提取技術(1) 判斷數據庫類型(2) 獲取數據庫名長度(3) 逐字符獲取數據庫名(4) 獲取…

OpenCV 圖形API(3)高層次設計概覽

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 描述 G-API 是一個異構框架&#xff0c;提供了統一的 API 來使用多個支持的后端編程圖像處理流水線。 關鍵的設計理念是在指定使用哪些內核和設備時保持流…

阿里云Tair KVCache:打造以緩存為中心的大模型Token超級工廠

一、Tair KVCache 簡介 Tair KVCache 是阿里云瑤池旗下云數據庫 Tair 面向大語言模型推理場景推出的 KVCache 緩存加速服務。 隨著互聯網技術的演進與流量規模的激增&#xff0c;緩存技術逐漸成為系統架構的核心組件。該階段催生了 Redis 等開源緩存數據庫&#xff0c;阿里巴巴…

Open GL ES ->GLSurfaceView正交投影與透視投影方法中近遠平面取值參考

坐標系 OpenGL ES使用右手坐標系&#xff0c;相機默認朝向負z方向 相機位置|vz軸<----- 0 -----> -near -----> -far -----不可見 可見區域 不可見裁剪規則 只有z值在[-near, -far]范圍內的物體可見&#xff0c; 當z > -near&#xff08;在近平面前&#…

iOS自定義collection view的page size(width/height)分頁效果

前言 想必大家工作中或多或少會遇到下圖樣式的UI需求吧 像這種cell長度不固定&#xff0c;并且還能實現的分頁效果UI還是很常見的 實現 我們這里實現主要采用collection view&#xff0c;實現的方式是自定義一個UICollectionViewFlowLayout的子類&#xff0c;在這個類里對…

Java高頻面試之并發編程-01

hello啊&#xff0c;各位觀眾姥爺們&#xff01;&#xff01;&#xff01;本baby今天來報道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面試官&#xff1a;并行跟并發有什么區別&#xff1f; 并發 vs 并行&#xff1a;核心區別與場景 1. 定義對比 維度并發&#xff08;Concu…

從零開始學Rust:所有權(Ownership)機制精要

文章目錄 第四章&#xff1a;Ownership 所有權核心概念關鍵機制引用與借用&#xff08;Reference & Borrowing&#xff09;懸垂引用問題錯誤示例分析解決方案引用安全規則 切片&#xff08;Slice&#xff09;內存安全保證 第四章&#xff1a;Ownership 所有權 Ownership i…

一旦懂得,有趣得緊1:詞根tempt-(嘗試)的兩種解法

詞根tempt-嘗試 tempt vt.引誘&#xff1b;誘惑&#xff1b;慫恿&#xff1b;利誘&#xff1b;勸誘&#xff1b;鼓動 temptation n.引誘&#xff1b;誘惑 // tempt v.引誘 -ation 名詞后綴 attempt v.&n.嘗試&#xff0c;試圖 // at- 加強 tempt 嘗試contempt n.蔑視&am…

召喚數學精靈

1.召喚數學精靈 - 藍橋云課 問題描述 數學家們發現了兩種用于召喚強大的數學精靈的儀式&#xff0c;這兩種儀式分別被稱為累加法儀式 A(n) 和累乘法儀式 B(n)。 累加法儀式 A(n) 是將從1到 n 的所有數字進行累加求和&#xff0c;即&#xff1a; A(n)12?n 累乘法儀式 B(n) …