在上一篇博文中,介绍了如果使用three.js加载gltf三维模型。这里介绍一下如何加载Obj模型。其实原理和方法都是类似的,只是换了一个解析的依赖 OBJLoader.js

多余的代码,不再写了,可以参考上一篇博文。

依赖OBJLoader.js

import {BufferGeometry,FileLoader,Float32BufferAttribute,Group,LineBasicMaterial,LineSegments,Loader,Material,Mesh,MeshPhongMaterial,Points,PointsMaterial,Vector3
} from 'three';// o object_name | g group_name
const _object_pattern = /^[og]\s*(.+)?/;
// mtllib file_reference
const _material_library_pattern = /^mtllib /;
// usemtl material_name
const _material_use_pattern = /^usemtl /;
// usemap map_name
const _map_use_pattern = /^usemap /;const _vA = new Vector3();
const _vB = new Vector3();
const _vC = new Vector3();const _ab = new Vector3();
const _cb = new Vector3();function ParserState() {const state = {objects: [],object: {},vertices: [],normals: [],colors: [],uvs: [],materials: {},materialLibraries: [],startObject: function ( name, fromDeclaration ) {// If the current object (initial from reset) is not from a g/o declaration in the parsed// file. We need to use it for the first parsed g/o to keep things in sync.if ( this.object && this.object.fromDeclaration === false ) {this.object.name = name;this.object.fromDeclaration = ( fromDeclaration !== false );return;}const previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );if ( this.object && typeof this.object._finalize === 'function' ) {this.object._finalize( true );}this.object = {name: name || '',fromDeclaration: ( fromDeclaration !== false ),geometry: {vertices: [],normals: [],colors: [],uvs: [],hasUVIndices: false},materials: [],smooth: true,startMaterial: function ( name, libraries ) {const previous = this._finalize( false );// New usemtl declaration overwrites an inherited material, except if faces were declared// after the material, then it must be preserved for proper MultiMaterial continuation.if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {this.materials.splice( previous.index, 1 );}const material = {index: this.materials.length,name: name || '',mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),smooth: ( previous !== undefined ? previous.smooth : this.smooth ),groupStart: ( previous !== undefined ? previous.groupEnd : 0 ),groupEnd: - 1,groupCount: - 1,inherited: false,clone: function ( index ) {const cloned = {index: ( typeof index === 'number' ? index : this.index ),name: this.name,mtllib: this.mtllib,smooth: this.smooth,groupStart: 0,groupEnd: - 1,groupCount: - 1,inherited: false};cloned.clone = this.clone.bind( cloned );return cloned;}};this.materials.push( material );return material;},currentMaterial: function () {if ( this.materials.length > 0 ) {return this.materials[ this.materials.length - 1 ];}return undefined;},_finalize: function ( end ) {const lastMultiMaterial = this.currentMaterial();if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) {lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;lastMultiMaterial.inherited = false;}// Ignore objects tail materials if no face declarations followed them before a new o/g started.if ( end && this.materials.length > 1 ) {for ( let mi = this.materials.length - 1; mi >= 0; mi -- ) {if ( this.materials[ mi ].groupCount <= 0 ) {this.materials.splice( mi, 1 );}}}// Guarantee at least one empty material, this makes the creation later more straight forward.if ( end && this.materials.length === 0 ) {this.materials.push( {name: '',smooth: this.smooth} );}return lastMultiMaterial;}};// Inherit previous objects material.// Spec tells us that a declared material must be set to all objects until a new material is declared.// If a usemtl declaration is encountered while this new object is being parsed, it will// overwrite the inherited material. Exception being that there was already face declarations// to the inherited material, then it will be preserved for proper MultiMaterial continuation.if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {const declared = previousMaterial.clone( 0 );declared.inherited = true;this.object.materials.push( declared );}this.objects.push( this.object );},finalize: function () {if ( this.object && typeof this.object._finalize === 'function' ) {this.object._finalize( true );}},parseVertexIndex: function ( value, len ) {const index = parseInt( value, 10 );return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;},parseNormalIndex: function ( value, len ) {const index = parseInt( value, 10 );return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;},parseUVIndex: function ( value, len ) {const index = parseInt( value, 10 );return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;},addVertex: function ( a, b, c ) {const src = this.vertices;const dst = this.object.geometry.vertices;dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );},addVertexPoint: function ( a ) {const src = this.vertices;const dst = this.object.geometry.vertices;dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );},addVertexLine: function ( a ) {const src = this.vertices;const dst = this.object.geometry.vertices;dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );},addNormal: function ( a, b, c ) {const src = this.normals;const dst = this.object.geometry.normals;dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );},addFaceNormal: function ( a, b, c ) {const src = this.vertices;const dst = this.object.geometry.normals;_vA.fromArray( src, a );_vB.fromArray( src, b );_vC.fromArray( src, c );_cb.subVectors( _vC, _vB );_ab.subVectors( _vA, _vB );_cb.cross( _ab );_cb.normalize();dst.push( _cb.x, _cb.y, _cb.z );dst.push( _cb.x, _cb.y, _cb.z );dst.push( _cb.x, _cb.y, _cb.z );},addColor: function ( a, b, c ) {const src = this.colors;const dst = this.object.geometry.colors;if ( src[ a ] !== undefined ) dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );if ( src[ b ] !== undefined ) dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );if ( src[ c ] !== undefined ) dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );},addUV: function ( a, b, c ) {const src = this.uvs;const dst = this.object.geometry.uvs;dst.push( src[ a + 0 ], src[ a + 1 ] );dst.push( src[ b + 0 ], src[ b + 1 ] );dst.push( src[ c + 0 ], src[ c + 1 ] );},addDefaultUV: function () {const dst = this.object.geometry.uvs;dst.push( 0, 0 );dst.push( 0, 0 );dst.push( 0, 0 );},addUVLine: function ( a ) {const src = this.uvs;const dst = this.object.geometry.uvs;dst.push( src[ a + 0 ], src[ a + 1 ] );},addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {const vLen = this.vertices.length;let ia = this.parseVertexIndex( a, vLen );let ib = this.parseVertexIndex( b, vLen );let ic = this.parseVertexIndex( c, vLen );this.addVertex( ia, ib, ic );this.addColor( ia, ib, ic );// normalsif ( na !== undefined && na !== '' ) {const nLen = this.normals.length;ia = this.parseNormalIndex( na, nLen );ib = this.parseNormalIndex( nb, nLen );ic = this.parseNormalIndex( nc, nLen );this.addNormal( ia, ib, ic );} else {this.addFaceNormal( ia, ib, ic );}// uvsif ( ua !== undefined && ua !== '' ) {const uvLen = this.uvs.length;ia = this.parseUVIndex( ua, uvLen );ib = this.parseUVIndex( ub, uvLen );ic = this.parseUVIndex( uc, uvLen );this.addUV( ia, ib, ic );this.object.geometry.hasUVIndices = true;} else {// add placeholder values (for inconsistent face definitions)this.addDefaultUV();}},addPointGeometry: function ( vertices ) {this.object.geometry.type = 'Points';const vLen = this.vertices.length;for ( let vi = 0, l = vertices.length; vi < l; vi ++ ) {const index = this.parseVertexIndex( vertices[ vi ], vLen );this.addVertexPoint( index );this.addColor( index );}},addLineGeometry: function ( vertices, uvs ) {this.object.geometry.type = 'Line';const vLen = this.vertices.length;const uvLen = this.uvs.length;for ( let vi = 0, l = vertices.length; vi < l; vi ++ ) {this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );}for ( let uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );}}};state.startObject( '', false );return state;}class OBJLoader extends Loader {constructor( manager ) {super( manager );this.materials = null;}load( url, onLoad, onProgress, onError ) {const scope = this;const loader = new FileLoader( this.manager );loader.setPath( this.path );loader.setRequestHeader( this.requestHeader );loader.setWithCredentials( this.withCredentials );loader.load( url, function ( text ) {try {onLoad( scope.parse( text ) );} catch ( e ) {if ( onError ) {onError( e );} else {console.error( e );}scope.manager.itemError( url );}}, onProgress, onError );}setMaterials( materials ) {this.materials = materials;return this;}parse( text ) {const state = new ParserState();if ( text.indexOf( '\r\n' ) !== - 1 ) {// This is faster than String.split with regex that splits on bothtext = text.replace( /\r\n/g, '\n' );}if ( text.indexOf( '\\\n' ) !== - 1 ) {// join lines separated by a line continuation character (\)text = text.replace( /\\\n/g, '' );}const lines = text.split( '\n' );let line = '', lineFirstChar = '';let lineLength = 0;let result = [];// Faster to just trim left side of the line. Use if available.const trimLeft = ( typeof ''.trimLeft === 'function' );for ( let i = 0, l = lines.length; i < l; i ++ ) {line = lines[ i ];line = trimLeft ? line.trimLeft() : line.trim();lineLength = line.length;if ( lineLength === 0 ) continue;lineFirstChar = line.charAt( 0 );// @todo invoke passed in handler if anyif ( lineFirstChar === '#' ) continue;if ( lineFirstChar === 'v' ) {const data = line.split( /\s+/ );switch ( data[ 0 ] ) {case 'v':state.vertices.push(parseFloat( data[ 1 ] ),parseFloat( data[ 2 ] ),parseFloat( data[ 3 ] ));if ( data.length >= 7 ) {state.colors.push(parseFloat( data[ 4 ] ),parseFloat( data[ 5 ] ),parseFloat( data[ 6 ] ));} else {// if no colors are defined, add placeholders so color and vertex indices matchstate.colors.push( undefined, undefined, undefined );}break;case 'vn':state.normals.push(parseFloat( data[ 1 ] ),parseFloat( data[ 2 ] ),parseFloat( data[ 3 ] ));break;case 'vt':state.uvs.push(parseFloat( data[ 1 ] ),parseFloat( data[ 2 ] ));break;}} else if ( lineFirstChar === 'f' ) {const lineData = line.substr( 1 ).trim();const vertexData = lineData.split( /\s+/ );const faceVertices = [];// Parse the face vertex data into an easy to work with formatfor ( let j = 0, jl = vertexData.length; j < jl; j ++ ) {const vertex = vertexData[ j ];if ( vertex.length > 0 ) {const vertexParts = vertex.split( '/' );faceVertices.push( vertexParts );}}// Draw an edge between the first vertex and all subsequent vertices to form an n-gonconst v1 = faceVertices[ 0 ];for ( let j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {const v2 = faceVertices[ j ];const v3 = faceVertices[ j + 1 ];state.addFace(v1[ 0 ], v2[ 0 ], v3[ 0 ],v1[ 1 ], v2[ 1 ], v3[ 1 ],v1[ 2 ], v2[ 2 ], v3[ 2 ]);}} else if ( lineFirstChar === 'l' ) {const lineParts = line.substring( 1 ).trim().split( ' ' );let lineVertices = [];const lineUVs = [];if ( line.indexOf( '/' ) === - 1 ) {lineVertices = lineParts;} else {for ( let li = 0, llen = lineParts.length; li < llen; li ++ ) {const parts = lineParts[ li ].split( '/' );if ( parts[ 0 ] !== '' ) lineVertices.push( parts[ 0 ] );if ( parts[ 1 ] !== '' ) lineUVs.push( parts[ 1 ] );}}state.addLineGeometry( lineVertices, lineUVs );} else if ( lineFirstChar === 'p' ) {const lineData = line.substr( 1 ).trim();const pointData = lineData.split( ' ' );state.addPointGeometry( pointData );} else if ( ( result = _object_pattern.exec( line ) ) !== null ) {// o object_name// or// g group_name// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869// let name = result[ 0 ].substr( 1 ).trim();const name = ( ' ' + result[ 0 ].substr( 1 ).trim() ).substr( 1 );state.startObject( name );} else if ( _material_use_pattern.test( line ) ) {// materialstate.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );} else if ( _material_library_pattern.test( line ) ) {// mtl filestate.materialLibraries.push( line.substring( 7 ).trim() );} else if ( _map_use_pattern.test( line ) ) {// the line is parsed but ignored since the loader assumes textures are defined MTL files// (according to https://www.okino.com/conv/imp_wave.htm, 'usemap' is the old-style Wavefront texture reference method)console.warn( 'THREE.OBJLoader: Rendering identifier "usemap" not supported. Textures must be defined in MTL files.' );} else if ( lineFirstChar === 's' ) {result = line.split( ' ' );// smooth shading// @todo Handle files that have varying smooth values for a set of faces inside one geometry,// but does not define a usemtl for each face set.// This should be detected and a dummy material created (later MultiMaterial and geometry groups).// This requires some care to not create extra material on each smooth value for "normal" obj files.// where explicit usemtl defines geometry groups.// Example asset: examples/models/obj/cerberus/Cerberus.obj/** http://paulbourke.net/dataformats/obj/* or* http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf** From chapter "Grouping" Syntax explanation "s group_number":* "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.* Polygonal elements use group numbers to put elements in different smoothing groups. For free-form* surfaces, smoothing groups are either turned on or off; there is no difference between values greater* than 0."*/if ( result.length > 1 ) {const value = result[ 1 ].trim().toLowerCase();state.object.smooth = ( value !== '0' && value !== 'off' );} else {// ZBrush can produce "s" lines #11707state.object.smooth = true;}const material = state.object.currentMaterial();if ( material ) material.smooth = state.object.smooth;} else {// Handle null terminated files without exceptionif ( line === '\0' ) continue;console.warn( 'THREE.OBJLoader: Unexpected line: "' + line + '"' );}}state.finalize();const container = new Group();container.materialLibraries = [].concat( state.materialLibraries );const hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 );if ( hasPrimitives === true ) {for ( let i = 0, l = state.objects.length; i < l; i ++ ) {const object = state.objects[ i ];const geometry = object.geometry;const materials = object.materials;const isLine = ( geometry.type === 'Line' );const isPoints = ( geometry.type === 'Points' );let hasVertexColors = false;// Skip o/g line declarations that did not follow with any facesif ( geometry.vertices.length === 0 ) continue;const buffergeometry = new BufferGeometry();buffergeometry.setAttribute( 'position', new Float32BufferAttribute( geometry.vertices, 3 ) );if ( geometry.normals.length > 0 ) {buffergeometry.setAttribute( 'normal', new Float32BufferAttribute( geometry.normals, 3 ) );}if ( geometry.colors.length > 0 ) {hasVertexColors = true;buffergeometry.setAttribute( 'color', new Float32BufferAttribute( geometry.colors, 3 ) );}if ( geometry.hasUVIndices === true ) {buffergeometry.setAttribute( 'uv', new Float32BufferAttribute( geometry.uvs, 2 ) );}// Create materialsconst createdMaterials = [];for ( let mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {const sourceMaterial = materials[ mi ];const materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;let material = state.materials[ materialHash ];if ( this.materials !== null ) {material = this.materials.create( sourceMaterial.name );// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.if ( isLine && material && ! ( material instanceof LineBasicMaterial ) ) {const materialLine = new LineBasicMaterial();Material.prototype.copy.call( materialLine, material );materialLine.color.copy( material.color );material = materialLine;} else if ( isPoints && material && ! ( material instanceof PointsMaterial ) ) {const materialPoints = new PointsMaterial( { size: 10, sizeAttenuation: false } );Material.prototype.copy.call( materialPoints, material );materialPoints.color.copy( material.color );materialPoints.map = material.map;material = materialPoints;}}if ( material === undefined ) {if ( isLine ) {material = new LineBasicMaterial();} else if ( isPoints ) {material = new PointsMaterial( { size: 1, sizeAttenuation: false } );} else {material = new MeshPhongMaterial();}material.name = sourceMaterial.name;material.flatShading = sourceMaterial.smooth ? false : true;material.vertexColors = hasVertexColors;state.materials[ materialHash ] = material;}createdMaterials.push( material );}// Create meshlet mesh;if ( createdMaterials.length > 1 ) {for ( let mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {const sourceMaterial = materials[ mi ];buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );}if ( isLine ) {mesh = new LineSegments( buffergeometry, createdMaterials );} else if ( isPoints ) {mesh = new Points( buffergeometry, createdMaterials );} else {mesh = new Mesh( buffergeometry, createdMaterials );}} else {if ( isLine ) {mesh = new LineSegments( buffergeometry, createdMaterials[ 0 ] );} else if ( isPoints ) {mesh = new Points( buffergeometry, createdMaterials[ 0 ] );} else {mesh = new Mesh( buffergeometry, createdMaterials[ 0 ] );}}mesh.name = object.name;container.add( mesh );}} else {if ( state.vertices.length > 0 ) {const material = new PointsMaterial( { size: 1, sizeAttenuation: false } );const buffergeometry = new BufferGeometry();buffergeometry.setAttribute( 'position', new Float32BufferAttribute( state.vertices, 3 ) );if ( state.colors.length > 0 && state.colors[ 0 ] !== undefined ) {buffergeometry.setAttribute( 'color', new Float32BufferAttribute( state.colors, 3 ) );material.vertexColors = true;}const points = new Points( buffergeometry, material );container.add( points );}}return container;}}export { OBJLoader };

实现逻辑

<template><div id="app"></div>
</template><script>
import {Scene, PerspectiveCamera, WebGLRenderer,  DirectionalLight} from 'three';
import {OBJLoader} from "@/lib/OBJLoader";
import {OrbitControls} from "@/lib/OrbitControls"export default {name: 'App',components: {},mounted() {let scene = new Scene();let camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);let renderer = new WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);let app = document.getElementById("app")app.appendChild(renderer.domElement);let loader = new OBJLoader();loader.load('./model/pearl.obj', function (obj) {let object = obj;object.children[0].material.color.set(0xe8b73b);object.rotation.x = 0;object.rotation.y = 0;console.log(object)object.position.x = 2object.position.y = -1scene.add(object);console.log('添加完成')},);//加载场景控制插件let controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.enableZoom = true;controls.autoRotate = false;controls.autoRotateSpeed = 3;controls.enablePan = true;controls.enableKeys = true;controls.keyPanSpeed = 7;controls.keys = {LEFT: 37,UP: 38,RIGHT: 39,BOTTOM: 40}this.controls = controls;//添加一个光源let light = new DirectionalLight(0xffffff);//光源颜色light.position.set(20, 10, 1305);//光源位置scene.add(light);//光源添加到场景中camera.position.z = 5;//渲染场景let animate = function () {requestAnimationFrame(animate);renderer.render(scene, camera);};animate();}
}
</script><style>
#app {}body, html {margin: 0;padding: 0;
}
</style>

具体效果

注意

网上还介绍了一种,先加载MTL,再加载OBJ的方法,经测试,三维模型也可以加载出来,但是材质并没有正常显示。

three.js加载Obj三维模型相关推荐

  1. VR 效果 前端使用 three.js 加载 Obj,drc文件(三维模型文件)

    VR 效果 前端使用 three.js 加载 Obj(三维模型文件) 前言:我对 three.js 并不熟!只是朋友有这个需求,写了几个 demo 运行,能看~.如果想深入学习,还需要多看看官方文档, ...

  2. Three.js加载.obj和.mtl文件(无法加载材质、路径错误问题)

    加载.obj模型文件 本文是Three.js电子书的14.3节 使用三维软件导出.obj模型文件的时候,会同时导出一个材质文件.mtl, .obj和.stl文件包含的信息一样都是几何体顶点相关数据,材 ...

  3. three.js加载OBJ模型

    three.js加载OBJ模型 推荐一个免费下载3D模型的网址https://www.cgtrader.com,包含多种格式(obj, mtl等). three.js现在是es6语法,旧版本是es5的 ...

  4. 首次使用three.js加载obj模型未成功

    接此,https://blog.csdn.net/bcbobo21cn/article/details/110676331 基本代码如下: <!DOCTYPE html> <html ...

  5. vue使用three.js加载obj和mtl

    ①构建三维坐标,代码如下 <!-- --> <template><div id="container"></div> </te ...

  6. threejs加载服务器文件,如何使用Three.js加载obj和mtl文件

    OBJ和MTL是3D模型的几何模型文件和材料文件. 在最新的three.js版本(r78)中,以前的OBJMTLLoader类已废弃. 现在要加载OBJ和MTL文件,需要结合OBJLoader和MTL ...

  7. three.js加载obj模型和材质

    1.Vue中安装three.js和加载用的包 安装three.js使用npm install three --save 安装加载obj和mtl文件的包npm install three-obj-mtl ...

  8. Vue引入Thee.js 加载obj文件和mtl文件并上纹理贴图

    目录 1.引入包三个包 2.在vue 组件内 import文件 3.在mound钩子函数内调用 4.效果图 部分加载报错 另:HTML页面使用three加载obj文件和mtl文件 1.引入包三个包 n ...

  9. 使用three.js加载obj+mtl文件

    因在项目中运用到WEBGL的技术.所以研究了一下下.three.js库看来十分强大,此处为加载带材质的obj文件 1.下载相关js    https://threejs.org/     thee.j ...

  10. three.js 加载obj+mtl模型

    本文提供了three.js中 实现将obj+mtl模型加载到场景中 的方法. 我们欲实现将桥模型加载到场景中,并对桥设置透明度: 实现过程: 分别导入three.js中的OBJLoader,MTLLo ...

最新文章

  1. 美国匹兹堡大学高伟教授招收Mobile AI方向全奖博士生
  2. 【转】:Andriod开发环境搭建 (1)
  3. 详解如何使用Istio监控基于容器的服务
  4. Android GridView的使用方法
  5. ECC椭圆曲线加密算法原理
  6. linux下dns服务器安装,Linux下DNS服务器安装配置方法详细介绍
  7. Eclipse关闭XML文件验证的方法
  8. 腾讯获准在中国销售Switch游戏机 任天堂股价应声飙升逾14%
  9. vue过滤器的那点事
  10. 评分卡模型开发(五)--定性指标筛选
  11. Asp.net core 通过Models 生成数据库的方法
  12. 高质量C /C编程指南---第2章 步调的版式
  13. nali工具解析ip来源
  14. volatility内存取证分析与讲解(持续更新)
  15. 网易云音乐登录信息加密算法详解
  16. 12分钟入门python基础-计算机视觉方向
  17. 【性能测试】性能测试指标TPS(Transaction per Second)
  18. java 线程池超时_线程池中如何控制超时时间?
  19. 黄淮学院CSDN高校俱乐部把梳子卖给和尚话剧表演活动
  20. Scrapy 简易爬取Boss直聘 可设定city job 爬取工作到excel或mysql中

热门文章

  1. 走进MSTP -- 4. OSN7500/7500II/3500/1500硬件速览
  2. PS分形图、人脸更换、蒙版技巧分享
  3. 简易检测wifi信号强度协助检测网络
  4. python 余弦值,Python向量余弦值 Python 求向量的余弦值操作
  5. ps安装了可以打开但开始里面找不到_PS CC2017安装教程【64/32位】
  6. DBSCAN聚类算法
  7. socket工具IP显示问题与连接不上问题
  8. 超级高铁(Hyperloop)
  9. 刷脸支付是在扫码支付的基础上发展而来
  10. MarkDown的下载、安装和基础使用